opencode-navigator 0.17.3 → 0.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,12 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## [0.18.0] - 2026-10-01
6
+
7
+ - Add durable per-session Todo lists to OpenCode 2.x through a separately configured Navigator server plugin and an agent Todo tool.
8
+ - Update Todo in the sidebar as tasks change, and offer an off-by-default per-session switch for model-context Todo instructions.
9
+ - Keep Todo visible with setup guidance when the OpenCode 2.x server plugin is missing.
10
+
5
11
  ## [0.17.3] - 2026-09-30
6
12
 
7
13
  ## [0.17.2] - 2026-09-30
@@ -30,6 +36,7 @@
30
36
 
31
37
  Earlier release notes are available on [GitHub Releases](https://github.com/St1ggy/opencode-navigator/releases).
32
38
 
39
+ [0.18.0]: https://github.com/St1ggy/opencode-navigator/compare/v0.17.3...v0.18.0
33
40
  [0.17.3]: https://github.com/St1ggy/opencode-navigator/compare/v0.17.2...v0.17.3
34
41
  [0.17.2]: https://github.com/St1ggy/opencode-navigator/compare/v0.17.1...v0.17.2
35
42
  [0.17.1]: https://github.com/St1ggy/opencode-navigator/compare/v0.17.0...v0.17.1
package/README.md CHANGED
@@ -12,7 +12,7 @@ See the [changelog](CHANGELOG.md) for release-by-release changes.
12
12
 
13
13
  - Theme-aware session title with the session creation date
14
14
  - First-run setup guide with interactive section settings
15
- - Collapsible Todo section with status filters, grouped tasks, progress and priority indicators
15
+ - Collapsible Todo section with status filters, grouped tasks, progress and priority indicators; OpenCode 2 adds session-owned tasks and optional model-context instructions
16
16
  - Active subagent list with live statuses and click-to-open navigation, including dev-team workers running in separate local processes
17
17
  - Compact, searchable workspace skill list with user-wide favorites, recent skills, source details, and click-to-confirm slash commands
18
18
  - Configurable host quick actions, including the auto-approve toggle, with user-wide bookmarks and availability reasons
@@ -42,8 +42,9 @@ collapsed. Use `Save current layout as default` in sidebar settings to reuse the
42
42
  current visible/hidden, expanded/collapsed, and ordering states in new sessions.
43
43
 
44
44
  Supports OpenCode 1.18.30 and newer, including OpenCode 2.x. OpenCode 2.0.16 does
45
- not expose Todo or LSP data to TUI plugins. Navigator keeps Todo visible with an
46
- explicit unsupported-host message and hides LSP on 2.x while retaining Subagents,
45
+ not expose host Todo or LSP data to TUI plugins. Navigator supplies its own
46
+ session Todo list through a separately configured server plugin in 2.x. Without
47
+ that plugin, Todo stays visible with setup guidance. LSP is hidden on 2.x while retaining Subagents,
47
48
  Skills, Quick Actions, MCP, Search, settings, and presets. All interface icons use Nerd Font glyphs by
48
49
  default (Codicons for controls, keyboard symbols for hints, language logos for LSP). Use a Nerd Fonts v3
49
50
  terminal font, or select **Settings → Behavior → Icon style → Text fallback**.
@@ -168,6 +169,26 @@ OpenCode 2.x CLI plugins are global terminal settings; there is no project-local
168
169
  plugin overrides are required. OpenCode validates `cli.json` strictly, so keep it
169
170
  within the linked schema and restart OpenCode after changing it.
170
171
 
172
+ To enable Todo in OpenCode 2.x, also install the **server** half of Navigator.
173
+ Add the same package to `~/.config/opencode/opencode.json` (or the project
174
+ `opencode.json`):
175
+
176
+ ```json
177
+ {
178
+ "$schema": "https://opencode.ai/config.json",
179
+ "plugin": ["opencode-navigator"]
180
+ }
181
+ ```
182
+
183
+ For a local source installation, use the directory containing Navigator's
184
+ `server.js` in the `plugin` array. Keep the TUI package in `cli.json` as above;
185
+ the server configuration alone does not replace it. Restart OpenCode after
186
+ configuring both. Todo items belong to the current session, survive restarts,
187
+ and update in the sidebar when the agent calls `navigator_todo_write`. The
188
+ **Todo instructions: Off/On** control in the Todo section is session-scoped and
189
+ starts **Off**: tasks and the agent tool work without adding Todo instructions
190
+ to the model's context. OpenCode 1.x continues to show the host's Todo list.
191
+
171
192
  ### Install the corner font
172
193
 
173
194
  **Required for rounded multiline selections in Nerd Font mode.** Run the font
@@ -509,8 +530,8 @@ root order. Activate any badge, including a custom server, to toggle its full ID
509
530
  in place. Navigator does not open a root/status dialog because OpenCode may report
510
531
  an empty root and does not expose the underlying diagnostic error text.
511
532
 
512
- Todo priorities are read-only because OpenCode does not expose a Todo mutation
513
- API to TUI plugins.
533
+ Todo priorities are read-only in the sidebar. OpenCode 1.x provides host Todo
534
+ data without a TUI mutation API; on 2.x, Navigator's agent tool writes tasks.
514
535
 
515
536
  Todo offers `All`, `Active`, and `Finished` views with counts. Active shows running
516
537
  tasks before pending tasks. Finished contains separate Completed and Cancelled
package/dist/rpc.js ADDED
@@ -0,0 +1,15 @@
1
+ var item={type:"object",properties:{content:{type:"string",minLength:1,maxLength:512},status:{type:"string",enum:["pending","in_progress","completed","cancelled"]},priority:{type:"string",enum:["high","medium","low"]}},required:["content","status"],additionalProperties:false
2
+ }
3
+ var todos={type:"array",items:item,maxItems:100}
4
+ var sessionID={type:"string",minLength:1}
5
+ var output={type:"object",properties:{todos,guidance:{type:"boolean"}},required:["todos","guidance"],additionalProperties:false
6
+ }
7
+ var NavigatorTodoRpc={id:"opencode-navigator.todo",methods:{list:{input:{type:"object",properties:{sessionID},required:["sessionID"],additionalProperties:false},output
8
+ },replace:{input:{type:"object",properties:{sessionID,todos},required:["sessionID","todos"],additionalProperties:false
9
+ },output
10
+ },guidance:{input:{type:"object",properties:{sessionID,enabled:{type:"boolean"}},required:["sessionID","enabled"],additionalProperties:false
11
+ },output
12
+ }},events:{updated:{schema:{type:"object",properties:{sessionID,todos,guidance:{type:"boolean"}},required:["sessionID","todos","guidance"],additionalProperties:false
13
+ }}}}
14
+ export{NavigatorTodoRpc
15
+ }
package/dist/server.js ADDED
@@ -0,0 +1,72 @@
1
+ import {Plugin} from "@opencode/plugin"
2
+ function normalizeTodoState(value){if(!value||typeof value!=="object")
3
+ return{todos:[],guidance:false}
4
+ const state=value
5
+ const statuses=new Set(["pending","in_progress","completed","cancelled"])
6
+ const priorities=new Set(["high","medium","low"])
7
+ const todos=Array.isArray(state.todos)?state.todos.filter((item)=>item&&typeof item.content==="string"&&item.content.trim().length>0&&item.content.length<=512&&statuses.has(item.status)).slice(0,100).map((item)=>({content:item.content.trim(),status:item.status,...priorities.has(item.priority??"")&&{priority:item.priority}})):[]
8
+ return{todos,guidance:state.guidance===true}}
9
+ function todoGuidance(state){const lines=state.todos.map((todo)=>`- [${todo.status}] ${todo.content}`)
10
+ return ["Navigator Todo: when work has multiple steps, use navigator_todo_write to keep the session Todo list current.","Mark work in progress, completed, or cancelled as it changes; avoid claiming unfinished work is done.",...lines.length>0?["Current Navigator Todo items:",...lines]:[]].join(`
11
+ `)
12
+ }
13
+ function key(sessionID){return `session.todo.${sessionID}`
14
+ }
15
+ function createManagedTodos(storage,onChange){const pending=new Map
16
+ async function list(sessionID){return normalizeTodoState(await storage.get(key(sessionID)))
17
+ }
18
+ function change(sessionID,apply){const request=(pending.get(sessionID)??Promise.resolve()).catch(()=>{}).then(async ()=>{const next=apply(await list(sessionID))
19
+ await storage.set(key(sessionID),next)
20
+ await onChange(sessionID,next)
21
+ return next
22
+ })
23
+ pending.set(sessionID,request)
24
+ const clear=()=>{if(pending.get(sessionID)===request)
25
+ pending.delete(sessionID)
26
+ }
27
+ request.then(clear).catch(clear)
28
+ return request
29
+ }
30
+ return{list,replace(sessionID,todos){return change(sessionID,(current)=>({...current,todos:normalizeTodoState({todos}).todos}))
31
+ },guidance(sessionID,enabled){return change(sessionID,(current)=>({...current,guidance:enabled}))
32
+ }}}
33
+ var item={type:"object",properties:{content:{type:"string",minLength:1,maxLength:512},status:{type:"string",enum:["pending","in_progress","completed","cancelled"]},priority:{type:"string",enum:["high","medium","low"]}},required:["content","status"],additionalProperties:false
34
+ }
35
+ var todos={type:"array",items:item,maxItems:100}
36
+ var sessionID={type:"string",minLength:1}
37
+ var output={type:"object",properties:{todos,guidance:{type:"boolean"}},required:["todos","guidance"],additionalProperties:false
38
+ }
39
+ var NavigatorTodoRpc={id:"opencode-navigator.todo",methods:{list:{input:{type:"object",properties:{sessionID},required:["sessionID"],additionalProperties:false},output
40
+ },replace:{input:{type:"object",properties:{sessionID,todos},required:["sessionID","todos"],additionalProperties:false
41
+ },output
42
+ },guidance:{input:{type:"object",properties:{sessionID,enabled:{type:"boolean"}},required:["sessionID","enabled"],additionalProperties:false
43
+ },output
44
+ }},events:{updated:{schema:{type:"object",properties:{sessionID,todos,guidance:{type:"boolean"}},required:["sessionID","todos","guidance"],additionalProperties:false
45
+ }}}};
46
+ var input={type:"object",properties:{todos:NavigatorTodoRpc.methods.replace.input.properties.todos
47
+ },required:["todos"],additionalProperties:false
48
+ }
49
+ var noNotify=async ()=>{}
50
+ var todo_server_default=Plugin.define({id:"opencode-navigator.todo-server",async setup(host){let notify=noNotify
51
+ const todos=createManagedTodos({get:(key)=>host.storage.get(key),set:(key,value)=>host.storage.set(key,value)
52
+ },(sessionID,state)=>notify(sessionID,state))
53
+ const rpc=await host.rpc.register(NavigatorTodoRpc,{list:async (value)=>todos.list(value.sessionID),replace:async (value)=>{const{sessionID,todos:items}=value
54
+ return todos.replace(sessionID,items)
55
+ },guidance:async (value)=>{const{sessionID,enabled}=value
56
+ return todos.guidance(sessionID,enabled)
57
+ }})
58
+ notify=(sessionID,state)=>rpc.events.emit("updated",{sessionID,...state})
59
+ const tools=await host.tool.transform((editor)=>{editor.namespace({name:"navigator",description:"Navigator session tasks"})
60
+ editor.add({name:"todo_write",description:"Replace the Navigator Todo list for this session with the current tasks and their statuses.",input,options:{namespace:"navigator"},execute:async (value,context)=>{const state=await todos.replace(context.sessionID,value.todos)
61
+ return{content:`Navigator Todo updated: ${state.todos.length} tasks.`}}})
62
+ })
63
+ const context=await host.session.hook("context",async (event)=>{const state=await todos.list(event.sessionID)
64
+ if(state.guidance)
65
+ event.system.push({type:"text",text:todoGuidance(state)})
66
+ })
67
+ return async ()=>{await context.dispose()
68
+ await tools.dispose()
69
+ await rpc.dispose()
70
+ }}})
71
+ export{todo_server_default as default
72
+ }
package/dist/tui.js CHANGED
@@ -2,7 +2,7 @@ import {insert as _$insert,setProp as _$setProp,createElement as _$createElement
2
2
 
3
3
 
4
4
 
5
- import {createSignal as createSignal2,createSignal,createEffect,onCleanup,createContext,useContext,createContext as createContext2,createSignal as createSignal3,useContext as useContext2,createEffect as createEffect2,splitProps,untrack,splitProps as splitProps2,For,createEffect as createEffect3,onCleanup as onCleanup2,Show,splitProps as splitProps3,createSignal as createSignal4,createSignal as createSignal5,createSignal as createSignal6,onCleanup as onCleanup3,batch,createSignal as createSignal8,createSignal as createSignal7,createEffect as createEffect4,createSignal as createSignal9,onCleanup as onCleanup4,createSignal as createSignal10,Index,createSignal as createSignal11,onCleanup as onCleanup5,createEffect as createEffect6,onCleanup as onCleanup7,batch as batch2,createEffect as createEffect5,createMemo,createSignal as createSignal12,onCleanup as onCleanup6,onMount,For as For2,For as For3,Show as Show2,createMemo as createMemo2,Show as Show3,createSignal as createSignal13,onCleanup as onCleanup8,For as For4,For as For5,For as For8,createMemo as createMemo4,onMount as onMount3,For as For7,createMemo as createMemo3,onMount as onMount2,Show as Show5,createEffect as createEffect7,createSignal as createSignal14,onCleanup as onCleanup9,Show as Show4,For as For6,For as For9,createEffect as createEffect8,createMemo as createMemo5,onCleanup as onCleanup10,For as For10,createEffect as createEffect9,createMemo as createMemo8,onCleanup as onCleanup11,onMount as onMount4,createMemo as createMemo6,createMemo as createMemo7,Show as Show6,onCleanup as onCleanup12,createSignal as createSignal15,createEffect as createEffect10,onCleanup as onCleanup13,onMount as onMount5,onCleanup as onCleanup14,onMount as onMount6,For as For11,createSignal as createSignal16,createSignal as createSignal17,Show as Show7,createSignal as createSignal19,createSignal as createSignal18,onCleanup as onCleanup15,For as For12,Show as Show10,createMemo as createMemo10,createComputed,createMemo as createMemo9,createSignal as createSignal20,untrack as untrack2,Show as Show8,createComputed as createComputed2,untrack as untrack3,Show as Show9,createEffect as createEffect11,createSignal as createSignal21,createMemo as createMemo12,createSignal as createSignal23,createMemo as createMemo11,For as For13,Show as Show13,Show as Show11,Show as Show12,createSignal as createSignal22,onCleanup as onCleanup16,For as For14,Show as Show15,createMemo as createMemo14,Show as Show14,createMemo as createMemo13,createEffect as createEffect12,onCleanup as onCleanup17,For as For19,Match,Switch,createMemo as createMemo18,onCleanup as onCleanup21,For as For15,Show as Show16,createSignal as createSignal24,createEffect as createEffect13,createMemo as createMemo15,onCleanup as onCleanup18,untrack as untrack4,For as For17,Show as Show19,createEffect as createEffect14,createMemo as createMemo16,createSignal as createSignal25,onCleanup as onCleanup19,untrack as untrack5,For as For16,Show as Show18,Show as Show17,Show as Show22,createEffect as createEffect15,createMemo as createMemo17,createSignal as createSignal26,onCleanup as onCleanup20,untrack as untrack6,For as For18,Show as Show21,Show as Show20,createEffect as createEffect16,createMemo as createMemo19,onCleanup as onCleanup22,untrack as untrack7,Show as Show23,createMemo as createMemo20,onCleanup as onCleanup23,createSignal as createSignal27,onCleanup as onCleanup24,onMount as onMount7} from "solid-js"
5
+ import {createSignal as createSignal2,createSignal,createEffect,onCleanup,createContext,useContext,createContext as createContext2,createSignal as createSignal3,useContext as useContext2,createEffect as createEffect2,splitProps,untrack,splitProps as splitProps2,For,createEffect as createEffect3,onCleanup as onCleanup2,Show,splitProps as splitProps3,createSignal as createSignal4,createSignal as createSignal5,createSignal as createSignal6,onCleanup as onCleanup3,batch,createSignal as createSignal8,createSignal as createSignal7,createEffect as createEffect4,createSignal as createSignal9,onCleanup as onCleanup4,createSignal as createSignal10,createSignal as createSignal11,Index,createSignal as createSignal12,onCleanup as onCleanup5,createEffect as createEffect6,onCleanup as onCleanup7,batch as batch2,createEffect as createEffect5,createMemo,createSignal as createSignal13,onCleanup as onCleanup6,onMount,For as For2,For as For3,Show as Show2,createMemo as createMemo2,Show as Show3,createSignal as createSignal14,onCleanup as onCleanup8,For as For4,For as For5,For as For8,createMemo as createMemo4,onMount as onMount3,For as For7,createMemo as createMemo3,onMount as onMount2,Show as Show5,createEffect as createEffect7,createSignal as createSignal15,onCleanup as onCleanup9,Show as Show4,For as For6,For as For9,createEffect as createEffect8,createMemo as createMemo5,onCleanup as onCleanup10,For as For10,createEffect as createEffect9,createMemo as createMemo8,onCleanup as onCleanup11,onMount as onMount4,createMemo as createMemo6,createMemo as createMemo7,Show as Show6,onCleanup as onCleanup12,createSignal as createSignal16,createEffect as createEffect10,onCleanup as onCleanup13,onMount as onMount5,onCleanup as onCleanup14,onMount as onMount6,For as For11,createSignal as createSignal17,createSignal as createSignal18,Show as Show7,createSignal as createSignal20,createSignal as createSignal19,onCleanup as onCleanup15,For as For12,Show as Show10,createMemo as createMemo10,createComputed,createMemo as createMemo9,createSignal as createSignal21,untrack as untrack2,Show as Show8,createComputed as createComputed2,untrack as untrack3,Show as Show9,createEffect as createEffect11,createSignal as createSignal22,createMemo as createMemo12,createSignal as createSignal24,createMemo as createMemo11,For as For13,Show as Show13,Show as Show11,Show as Show12,createSignal as createSignal23,onCleanup as onCleanup16,For as For14,Show as Show15,createMemo as createMemo14,Show as Show14,createMemo as createMemo13,createEffect as createEffect12,onCleanup as onCleanup17,For as For19,Match,Switch,createMemo as createMemo18,onCleanup as onCleanup21,For as For15,Show as Show16,createSignal as createSignal25,createEffect as createEffect13,createMemo as createMemo15,onCleanup as onCleanup18,untrack as untrack4,For as For17,Show as Show19,createEffect as createEffect14,createMemo as createMemo16,createSignal as createSignal26,onCleanup as onCleanup19,untrack as untrack5,For as For16,Show as Show18,Show as Show17,Show as Show23,createEffect as createEffect15,createMemo as createMemo17,createSignal as createSignal27,onCleanup as onCleanup20,untrack as untrack6,For as For18,Show as Show21,Show as Show20,Show as Show22,createEffect as createEffect16,createMemo as createMemo19,onCleanup as onCleanup22,untrack as untrack7,Show as Show24,createMemo as createMemo20,onCleanup as onCleanup23,createSignal as createSignal28,onCleanup as onCleanup24,onMount as onMount7} from "solid-js"
6
6
  var SIDEBAR_SECTIONS=["todo","subagents","skills","quick_actions","lsp","mcp"]
7
7
  var QUICK_ACTION_IDS=["session.rename","session.timeline","session.copy","session.export","session.compact","session.list","session.new","session.fork","messages.copy","session.first","session.last","session.messages_last_user","session.message.next","session.message.previous","permission.mode"]
8
8
  function preferencesScope(path){return path.worktree||path.directory||"global"
@@ -2619,6 +2619,99 @@ activeTarget=undefined
2619
2619
  requests.abortAll()
2620
2620
  refreshing.clear()
2621
2621
  }}}}
2622
+ function normalizeTodoState(value){if(!value||typeof value!=="object")
2623
+ return{todos:[],guidance:false}
2624
+ const state=value
2625
+ const statuses=new Set(["pending","in_progress","completed","cancelled"])
2626
+ const priorities=new Set(["high","medium","low"])
2627
+ const todos=Array.isArray(state.todos)?state.todos.filter((item)=>item&&typeof item.content==="string"&&item.content.trim().length>0&&item.content.length<=512&&statuses.has(item.status)).slice(0,100).map((item)=>({content:item.content.trim(),status:item.status,...priorities.has(item.priority??"")&&{priority:item.priority}})):[]
2628
+ return{todos,guidance:state.guidance===true}}
2629
+
2630
+ var item={type:"object",properties:{content:{type:"string",minLength:1,maxLength:512},status:{type:"string",enum:["pending","in_progress","completed","cancelled"]},priority:{type:"string",enum:["high","medium","low"]}},required:["content","status"],additionalProperties:false
2631
+ }
2632
+ var todos={type:"array",items:item,maxItems:100}
2633
+ var sessionID={type:"string",minLength:1}
2634
+ var output={type:"object",properties:{todos,guidance:{type:"boolean"}},required:["todos","guidance"],additionalProperties:false
2635
+ }
2636
+ var NavigatorTodoRpc={id:"opencode-navigator.todo",methods:{list:{input:{type:"object",properties:{sessionID},required:["sessionID"],additionalProperties:false},output
2637
+ },replace:{input:{type:"object",properties:{sessionID,todos},required:["sessionID","todos"],additionalProperties:false
2638
+ },output
2639
+ },guidance:{input:{type:"object",properties:{sessionID,enabled:{type:"boolean"}},required:["sessionID","enabled"],additionalProperties:false
2640
+ },output
2641
+ }},events:{updated:{schema:{type:"object",properties:{sessionID,todos,guidance:{type:"boolean"}},required:["sessionID","todos","guidance"],additionalProperties:false
2642
+ }}}};
2643
+ function createNavigatorTodoController(context,api){const rpc=context.client.rpc(NavigatorTodoRpc)
2644
+ const [sessions,setSessions]=createSignal11({})
2645
+ const [saving,setSaving]=createSignal11({})
2646
+ const [errors,setErrors]=createSignal11({})
2647
+ const requests=createRequestState(api.lifecycle.signal)
2648
+ const refreshing=new Map
2649
+ const revisions=new Map
2650
+ let activeTarget
2651
+ function target(sessionID){const session=api.state.session.get(sessionID)
2652
+ const directory=session?.directory??api.state.path.directory
2653
+ const workspace=session?.workspaceID
2654
+ return{key:JSON.stringify([sessionID,directory,workspace??null]),sessionID,routing:{directory}}}
2655
+ function set(key,value){setSessions((current)=>({...current,[key]:normalizeTodoState(value)}))
2656
+ }
2657
+ function refresh(sessionID,force=false){const current=target(sessionID)
2658
+ const pending=refreshing.get(current.key)
2659
+ if(pending&&!force)
2660
+ return pending
2661
+ const revision=revisions.get(current.key)??0
2662
+ const requestState=requests.start(current.key,"refresh Navigator todos",current.key in sessions(),force)
2663
+ if(!requestState)
2664
+ return pending
2665
+ const request=rpc.list({sessionID},{location:current.routing,signal:requestState.signal}).then((value)=>{const state=normalizeTodoState(value)
2666
+ if(requestState.isCurrent()&&(revisions.get(current.key)??0)===revision)
2667
+ set(current.key,state)
2668
+ requestState.succeed()
2669
+ return state
2670
+ }).catch((error)=>{requestState.fail(error)
2671
+ throw error
2672
+ }).finally(()=>{requestState.finish()
2673
+ if(refreshing.get(current.key)===request)
2674
+ refreshing.delete(current.key)
2675
+ })
2676
+ refreshing.set(current.key,request)
2677
+ return request
2678
+ }
2679
+ const unsubscribe=rpc.events.on("updated",(event)=>{const{sessionID}=event.data
2680
+ if(typeof sessionID!=="string")
2681
+ return
2682
+ const current=target(sessionID)
2683
+ if(event.location.directory!==current.routing.directory)
2684
+ return
2685
+ revisions.set(current.key,(revisions.get(current.key)??0) + 1)
2686
+ set(current.key,normalizeTodoState(event.data))
2687
+ })
2688
+ api.lifecycle.onDispose(unsubscribe)
2689
+ return{target,list(sessionID){return sessions()[target(sessionID).key]?.todos??[]
2690
+ },refresh,state(sessionID){return requests.state(target(sessionID).key)
2691
+ },retry(sessionID){return refresh(sessionID,true)
2692
+ },guidance(sessionID){const key=target(sessionID).key
2693
+ return{enabled:sessions()[key]?.guidance??false,saving:saving()[key]??false,error:errors()[key],available:key in sessions()
2694
+ }},async setGuidance(sessionID,enabled){const current=target(sessionID)
2695
+ if(!(current.key in sessions())||saving()[current.key])
2696
+ return
2697
+ setSaving((value)=>({...value,[current.key]:true}))
2698
+ setErrors((value)=>({...value,[current.key]:undefined}))
2699
+ try{const state=await rpc.guidance({sessionID,enabled},{location:current.routing})
2700
+ revisions.set(current.key,(revisions.get(current.key)??0) + 1)
2701
+ set(current.key,normalizeTodoState(state))
2702
+ } catch(error){setErrors((value)=>({...value,[current.key]:error instanceof Error?error.message:String(error)}))
2703
+ } finally{setSaving((value)=>({...value,[current.key]:false}))
2704
+ }},activate(sessionID){const key=target(sessionID).key
2705
+ if(activeTarget!==key){requests.abortAll()
2706
+ refreshing.clear()
2707
+ activeTarget=key
2708
+ }
2709
+ return()=>{if(activeTarget!==key)
2710
+ return
2711
+ activeTarget=undefined
2712
+ requests.abortAll()
2713
+ refreshing.clear()
2714
+ }}}}
2622
2715
  function buildTodoView(todos,mode){const active=[...todos.filter((item)=>item.status==="in_progress"),...todos.filter((item)=>item.status==="pending")]
2623
2716
  const completed=todos.filter((item)=>item.status==="completed")
2624
2717
  const cancelled=todos.filter((item)=>item.status==="cancelled")
@@ -2673,7 +2766,7 @@ return action.label
2673
2766
  const command=api.keymap.getCommands?.({visibility:"registered"}).find((candidate)=>candidate.name===action.command)
2674
2767
  return typeof command?.title==="string"?command.title:action.label
2675
2768
  }
2676
- function createQuickActionRevision(api){const [revision,setRevision]=createSignal11(0)
2769
+ function createQuickActionRevision(api){const [revision,setRevision]=createSignal12(0)
2677
2770
  const unsubscribe=api.keymap.on?.("state",()=>setRevision((value)=>value + 1))
2678
2771
  if(unsubscribe)
2679
2772
  onCleanup5(unsubscribe)
@@ -2771,7 +2864,7 @@ const [selectedID,setSelectedID]=useDialogState("selection",undefined)
2771
2864
  const [views]=useDialogState("tab-views",new Map)
2772
2865
  const tabViews=views()
2773
2866
  const [scrollTop,setScrollTop]=useDialogState("scroll",0)
2774
- const [refreshing,setRefreshing]=createSignal12(false)
2867
+ const [refreshing,setRefreshing]=createSignal13(false)
2775
2868
  const theme=()=>props.api.theme.current
2776
2869
  const icons=useIcons()
2777
2870
  const commandRevision=createQuickActionRevision(props.api)
@@ -3397,8 +3490,8 @@ return _el$
3397
3490
  }
3398
3491
  function FirstRunWizard(props){const icons=useIcons()
3399
3492
  const dialogs=useDialogs(props.api)
3400
- const [active,setActive]=createSignal13(0)
3401
- const [step,setStep]=createSignal13(0)
3493
+ const [active,setActive]=createSignal14(0)
3494
+ const [step,setStep]=createSignal14(0)
3402
3495
  const theme=()=>props.api.theme.current
3403
3496
  const sections=SECTION_DEFINITIONS.filter((section)=>supportsSidebarSection(props.api,section.name))
3404
3497
  const finishIndex=sections.length
@@ -3586,7 +3679,7 @@ const dialogs=useDialogs(props.api)
3586
3679
  const theme=()=>props.api.theme.current
3587
3680
  const dimensions=useTerminalDimensions4()
3588
3681
  const scroll=useDialogScroll()
3589
- const [active,setActive]=createSignal14("apply")
3682
+ const [active,setActive]=createSignal15("apply")
3590
3683
  let body
3591
3684
  const prefix=`${PLUGIN_ID}.preset-preview`
3592
3685
  function activate(action){if(!props.valid()){dialogs.close()
@@ -4628,7 +4721,7 @@ return _$createElement("box")
4628
4721
 
4629
4722
 
4630
4723
  function SettingsFooterButton(props){const icons=useIcons()
4631
- const [hovered,setHovered]=createSignal15(false)
4724
+ const [hovered,setHovered]=createSignal16(false)
4632
4725
  const theme=()=>props.api.theme.current
4633
4726
  return _$createComponent(IconControl,{id:`${PLUGIN_ID}.settings.footer`,get icon(){return icons.icon("settings")
4634
4727
  },get foregroundColor(){return _$memo(()=>!!hovered())()?theme().text:theme().textMuted
@@ -4849,8 +4942,8 @@ return _$createElement("box")
4849
4942
 
4850
4943
  var NAVIGATOR_PACKAGE_URL="https://registry.npmjs.org/opencode-navigator/latest"
4851
4944
  var REQUEST_TIMEOUT_MS=5000
4852
- function createVersionStatus(api,navigatorVersion,request=fetch){const [openCodeUpdate,setOpenCodeUpdate]=createSignal16()
4853
- const [navigatorUpdate,setNavigatorUpdate]=createSignal16()
4945
+ function createVersionStatus(api,navigatorVersion,request=fetch){const [openCodeUpdate,setOpenCodeUpdate]=createSignal17()
4946
+ const [navigatorUpdate,setNavigatorUpdate]=createSignal17()
4854
4947
  const controller=new AbortController
4855
4948
  const timeout=setTimeout(()=>controller.abort(),REQUEST_TIMEOUT_MS)
4856
4949
  const unsubscribe=api.event.on("installation.update-available",(event)=>{const version=event.properties.version
@@ -5013,9 +5106,9 @@ return false
5013
5106
  return true
5014
5107
  }
5015
5108
  function createSidebarInteraction(api,showHelp=()=>openKeyboardHelp(api)){const descriptors=new Map
5016
- const [selectedId,setSelectedId]=createSignal17()
5017
- const [focusWithin,setFocusWithin]=createSignal17(false)
5018
- const [revision,setRevision]=createSignal17(0)
5109
+ const [selectedId,setSelectedId]=createSignal18()
5110
+ const [focusWithin,setFocusWithin]=createSignal18(false)
5111
+ const [revision,setRevision]=createSignal18(0)
5019
5112
  let titleRoot
5020
5113
  let contentRoot
5021
5114
  let targetedRoot
@@ -5284,7 +5377,7 @@ else if(highlighted)
5284
5377
  foregroundColor=theme.selectedListItemText
5285
5378
  return{backgroundColor,foregroundColor}}
5286
5379
  function useSidebarItem(api,interaction,descriptor,normalColor=()=>api.theme.current.text,mode="row"){let unregister
5287
- const [hovered,setHovered]=createSignal18(false)
5380
+ const [hovered,setHovered]=createSignal19(false)
5288
5381
  const disabled=()=>descriptor.disabled?.()===true
5289
5382
  const focused=()=>Boolean(interaction?.ownsFocus()&&interaction.isSelected(descriptor.id))
5290
5383
  const colors=()=>sidebarInteractiveColors(api.theme.current,{focused:focused(),hovered:hovered(),disabled:disabled()},normalColor(),mode)
@@ -5302,7 +5395,7 @@ return true
5302
5395
  }}}
5303
5396
  function LspBadge(props){const icons=useIcons()
5304
5397
  const isKnown=lspIconName(props.id)!==undefined
5305
- const [showName,setShowName]=createSignal19(false)
5398
+ const [showName,setShowName]=createSignal20(false)
5306
5399
  const statusColor=()=>props.status==="connected"?props.api.theme.current.success:props.api.theme.current.error
5307
5400
  if(!isKnown){return(()=>{var _el$=_$createElement("text")
5308
5401
  _$setProp(_el$,"flexShrink",1)
@@ -5345,7 +5438,7 @@ return _el$3
5345
5438
 
5346
5439
 
5347
5440
 
5348
- function createListVisibility(options){const [expanded,setExpanded]=createSignal20(false)
5441
+ function createListVisibility(options){const [expanded,setExpanded]=createSignal21(false)
5349
5442
  let previousKey
5350
5443
  let previousLimit
5351
5444
  const canToggle=createMemo9(()=>options.limit()>0&&options.items().length>options.limit())
@@ -5427,7 +5520,7 @@ result += segment
5427
5520
  return result + ellipsis
5428
5521
  }
5429
5522
  function Section(props){const icons=useIcons()
5430
- const [actionWidth,setActionWidth]=createSignal21()
5523
+ const [actionWidth,setActionWidth]=createSignal22()
5431
5524
  const icon=()=>props.section?icons.section(props.section):props.icon
5432
5525
  const theme=()=>props.api.theme.current
5433
5526
  const item=useSidebarItem(props.api,props.interaction,{id:props.sectionId??`section.${props.title}`,position:()=>({section:props.navigationSection??0,row:0,column:0
@@ -5778,7 +5871,7 @@ return _el$2
5778
5871
  function SectionFilter(props){const icons=useIcons()
5779
5872
  let input
5780
5873
  let unregisterInput
5781
- const [inputFocused,setInputFocused]=createSignal22(false)
5874
+ const [inputFocused,setInputFocused]=createSignal23(false)
5782
5875
  const theme=()=>props.api.theme.current
5783
5876
  const item=useSidebarItem(props.api,props.interaction,{id:props.id??`filter.${props.placeholder}`,position:()=>props.position??{section:0,row:0,column:0
5784
5877
  },activate:()=>{if(!input)
@@ -5953,7 +6046,7 @@ return _el$
5953
6046
  }})
5954
6047
  }
5955
6048
  function McpSection(props){const icons=useIcons()
5956
- const [query,setQuery]=createSignal23("")
6049
+ const [query,setQuery]=createSignal24("")
5957
6050
  const model=createMcpSectionModel(props,query)
5958
6051
  const navigationSection=()=>props.navigationSection??6
5959
6052
  const summary=createMemo12(()=>`${model.active()}/${model.list().length}${model.errors() ? ` \xB7 ${model.errors()} ${icons.icon("error")}` : ""}`)
@@ -6299,7 +6392,7 @@ return _el$2
6299
6392
  }})
6300
6393
  }
6301
6394
  function SkillsSection(props){const dialogs=useDialogs(props.api)
6302
- const [query,setQuery]=createSignal24("")
6395
+ const [query,setQuery]=createSignal25("")
6303
6396
  const model=createSkillSectionModel(props,query)
6304
6397
  const navigationSection=()=>props.navigationSection??3
6305
6398
  function selectSkill(item){if(!props.preferences.shouldConfirmSkill(item)){model.useSkill(item)
@@ -6374,8 +6467,8 @@ return _el$
6374
6467
  function createSubagentSectionModel(props){const list=createMemo16(()=>props.controller.list(props.sessionID))
6375
6468
  const recent=createMemo16(()=>props.controller.recent?.(props.sessionID)??[])
6376
6469
  const rows=createMemo16(()=>buildSubagentView(list(),recent()))
6377
- const [query,setQuery]=createSignal25("")
6378
- const [mode,setMode]=createSignal25("all")
6470
+ const [query,setQuery]=createSignal26("")
6471
+ const [mode,setMode]=createSignal26("all")
6379
6472
  const counts=createMemo16(()=>countSubagentView(rows(),query()))
6380
6473
  const tabs=createMemo16(()=>["all","active","recent",...counts().errors>0?["errors"]:[]])
6381
6474
  const filtered=createMemo16(()=>filterSubagentView(rows(),mode(),query()))
@@ -6657,6 +6750,7 @@ return _el$
6657
6750
  })()
6658
6751
  }
6659
6752
  function TodoContents(props){const navigationSection=()=>props.navigationSection??1
6753
+ const tabsRow=()=>props.managed?3:2
6660
6754
  return(()=>{var _el$=_$createElement("box"),_el$2=_$createElement("box")
6661
6755
  _$insertNode(_el$,_el$2)
6662
6756
  _$setProp(_el$,"gap",1)
@@ -6664,7 +6758,7 @@ _$setProp(_el$2,"flexDirection","row")
6664
6758
  _$setProp(_el$2,"flexWrap","wrap")
6665
6759
  _$insert(_el$2,_$createComponent(For18,{each:["all","active","finished"],children:(value,index)=>_$createComponent(SectionTab,{get api(){return props.api
6666
6760
  },get interaction(){return props.interaction
6667
- },id:`${PLUGIN_ID}.todo.filter.${value}`,get position(){return{section:navigationSection(),row:2,column:index()
6761
+ },id:`${PLUGIN_ID}.todo.filter.${value}`,get position(){return{section:navigationSection(),row:tabsRow(),column:index()
6668
6762
  }},get label(){return value[0].toUpperCase() + value.slice(1)
6669
6763
  },get count(){return props.view.counts[value]
6670
6764
  },get selected(){return props.mode===value
@@ -6698,7 +6792,8 @@ return _el$3
6698
6792
  _$insert(_el$,_$createComponent(ListVisibilityControl,{get api(){return props.api
6699
6793
  },get interaction(){return props.interaction
6700
6794
  },section:"todo",get navigationSection(){return navigationSection()
6701
- },row:3,get visibility(){return props.visibility
6795
+ },get row(){return tabsRow() + 1
6796
+ },get visibility(){return props.visibility
6702
6797
  }}),null)
6703
6798
  return _el$
6704
6799
  })()
@@ -6709,10 +6804,39 @@ _$effect((_$p)=>_$setProp(_el$6,"fg",props.api.theme.current.textMuted,_$p))
6709
6804
  return _el$6
6710
6805
  })()
6711
6806
  }
6807
+
6808
+
6809
+
6810
+
6811
+
6812
+
6813
+ function TodoGuidanceControl(props){const state=()=>props.controller.guidance(props.sessionID)
6814
+ return _$createComponent(Show22,{get when(){return state().available
6815
+ },get children(){var _el$=_$createElement("box")
6816
+ _$insert(_el$,_$createComponent(SectionTab,{get api(){return props.api
6817
+ },get interaction(){return props.interaction
6818
+ },id:`${PLUGIN_ID}.todo.guidance`,get position(){return{section:props.navigationSection,row:2,column:0
6819
+ }},get label(){return `Todo instructions: ${state().enabled ? "On" : "Off"}`
6820
+ },get selected(){return state().enabled
6821
+ },get disabled(){return state().saving
6822
+ },onActivate:()=>void props.controller.setGuidance(props.sessionID,!state().enabled)
6823
+ }),null)
6824
+ _$insert(_el$,_$createComponent(Show22,{get when(){return state().error
6825
+ },children:(message)=>(()=>{var _el$2=_$createElement("text")
6826
+ _$setProp(_el$2,"wrapMode","word")
6827
+ _$insert(_el$2,message)
6828
+ _$effect((_$p)=>_$setProp(_el$2,"fg",props.api.theme.current.error,_$p))
6829
+ return _el$2
6830
+ })()
6831
+ }),null)
6832
+ return _el$
6833
+ }})
6834
+ }
6712
6835
  function TodoSection(props){const list=createMemo17(()=>props.controller.list(props.sessionID))
6713
6836
  const icons=useIcons()
6714
6837
  const unavailable=()=>hostCapabilityUnavailable(props.api,"todo")
6715
- const [mode,setMode]=createSignal26("all")
6838
+ const managed=()=>("guidance" in props.controller)?props.controller:undefined
6839
+ const [mode,setMode]=createSignal27("all")
6716
6840
  const targetKey=createMemo17(()=>props.controller.target?.(props.sessionID).key??props.sessionID)
6717
6841
  createEffect15(()=>{targetKey()
6718
6842
  setMode("all")
@@ -6735,28 +6859,48 @@ return _$createComponent(Section,{get api(){return props.api
6735
6859
  },sectionId:`${PLUGIN_ID}.section.todo`,get navigationSection(){return navigationSection()
6736
6860
  },title:"TODO",section:"todo",get summary(){return _$memo(()=>!!unavailable())()?"\u2014":`${view().counts.completed}/${list().length}`
6737
6861
  },get open(){return props.preferences.expanded().todo
6738
- },onToggle:()=>props.preferences.toggleSectionExpanded("todo"),get children(){return _$createComponent(Show22,{get when(){return unavailable()
6739
- },get fallback(){return _$createComponent(SectionRequestBody,{get api(){return props.api
6862
+ },onToggle:()=>props.preferences.toggleSectionExpanded("todo"),get children(){return _$createComponent(Show23,{get when(){return unavailable()
6863
+ },get fallback(){return(()=>{var _el$=_$createElement("box")
6864
+ _$setProp(_el$,"gap",1)
6865
+ _$insert(_el$,_$createComponent(Show23,{get when(){return managed()?.guidance(props.sessionID).available
6866
+ },get children(){return _$createComponent(TodoGuidanceControl,{get api(){return props.api
6867
+ },get controller(){return managed()
6868
+ },get sessionID(){return props.sessionID
6869
+ },get interaction(){return props.interaction
6870
+ },get navigationSection(){return navigationSection()
6871
+ }})
6872
+ }}),null)
6873
+ _$insert(_el$,_$createComponent(Show23,{get when(){return _$memo(()=>!!(managed()&&state().error))()&&!managed()?.guidance(props.sessionID).available
6874
+ },get children(){var _el$2=_$createElement("text")
6875
+ _$insertNode(_el$2,_$createTextNode(`Add Navigator's server plugin to opencode.json; restart OpenCode to enable Todo.`))
6876
+ _$setProp(_el$2,"wrapMode","word")
6877
+ _$effect((_$p)=>_$setProp(_el$2,"fg",props.api.theme.current.textMuted,_$p))
6878
+ return _el$2
6879
+ }}),null)
6880
+ _$insert(_el$,_$createComponent(SectionRequestBody,{get api(){return props.api
6740
6881
  },get interaction(){return props.interaction
6741
6882
  },id:`${PLUGIN_ID}.retry.todo`,get position(){return{section:navigationSection(),row:1,column:0
6742
6883
  }},get state(){return state()
6743
6884
  },get hasItems(){return list().length>0
6744
- },empty:"No tasks yet",loading:"Loading tasks\u2026",onRetry:()=>void props.controller.retry(props.sessionID),get children(){return _$createComponent(TodoContents,{get api(){return props.api
6885
+ },empty:"No tasks yet",loading:"Loading tasks\u2026",onRetry:()=>void props.controller.retry(props.sessionID).catch(()=>{}),get children(){return _$createComponent(TodoContents,{get api(){return props.api
6745
6886
  },get interaction(){return props.interaction
6746
6887
  },get navigationSection(){return props.navigationSection
6747
6888
  },get mode(){return mode()
6748
6889
  },setMode,get view(){return view()
6749
6890
  },visibility,get density(){return props.preferences.rowDensity?.()??"compact"
6891
+ },get managed(){return Boolean(managed())
6750
6892
  }})
6751
- }})
6752
- },children:(message)=>(()=>{var _el$=_$createElement("text"),_el$2=_$createTextNode(` `)
6753
- _$insertNode(_el$,_el$2)
6754
- _$setProp(_el$,"wrapMode","word")
6755
- _$insert(_el$,()=>icons.icon("info"),_el$2)
6756
- _$insert(_el$,message,null)
6757
- _$effect((_$p)=>_$setProp(_el$,"fg",props.api.theme.current.textMuted,_$p))
6893
+ }}),null)
6758
6894
  return _el$
6759
6895
  })()
6896
+ },children:(message)=>(()=>{var _el$4=_$createElement("text"),_el$5=_$createTextNode(` `)
6897
+ _$insertNode(_el$4,_el$5)
6898
+ _$setProp(_el$4,"wrapMode","word")
6899
+ _$insert(_el$4,()=>icons.icon("info"),_el$5)
6900
+ _$insert(_el$4,message,null)
6901
+ _$effect((_$p)=>_$setProp(_el$4,"fg",props.api.theme.current.textMuted,_$p))
6902
+ return _el$4
6903
+ })()
6760
6904
  })
6761
6905
  }})
6762
6906
  }
@@ -6868,7 +7012,7 @@ _$setProp(_el$2,"flexDirection","row")
6868
7012
  _$setProp(_el$2,"justifyContent","space-between")
6869
7013
  _$setProp(_el$2,"gap",1)
6870
7014
  _$setProp(_el$3,"flexGrow",1)
6871
- _$insert(_el$3,_$createComponent(Show23,{get when(){return props.preferences.showSessionTitle()
7015
+ _$insert(_el$3,_$createComponent(Show24,{get when(){return props.preferences.showSessionTitle()
6872
7016
  },get children(){var _el$4=_$createElement("text"),_el$5=_$createElement("b")
6873
7017
  _$insertNode(_el$4,_el$5)
6874
7018
  _$setProp(_el$4,"wrapMode","word")
@@ -6876,7 +7020,7 @@ _$insert(_el$5,()=>props.title)
6876
7020
  _$effect((_$p)=>_$setProp(_el$4,"fg",theme().text,_$p))
6877
7021
  return _el$4
6878
7022
  }}),null)
6879
- _$insert(_el$3,_$createComponent(Show23,{get when(){return _$memo(()=>!!props.preferences.showSessionDate())()&&created()
7023
+ _$insert(_el$3,_$createComponent(Show24,{get when(){return _$memo(()=>!!props.preferences.showSessionDate())()&&created()
6880
7024
  },children:(value)=>(()=>{var _el$6=_$createElement("text")
6881
7025
  _$insert(_el$6,value)
6882
7026
  _$effect((_$p)=>_$setProp(_el$6,"fg",theme().textMuted,_$p))
@@ -7007,7 +7151,7 @@ return false
7007
7151
  }
7008
7152
  function enabled(value){return typeof value==="function"?value():value!==false
7009
7153
  }
7010
- function createV2Keymap(context){const [revision,setRevision]=createSignal27(0)
7154
+ function createV2Keymap(context){const [revision,setRevision]=createSignal28(0)
7011
7155
  const layers=new Map
7012
7156
  let sequence=0
7013
7157
  function layerActive(layer){const mode=layer.mode??"base"
@@ -7191,8 +7335,8 @@ context.ui.router.navigate({type:"plugin",name,data:params})
7191
7335
  },attention:context.attention,lifecycle:{signal:controller.signal,onDispose}}
7192
7336
  api.slots=createV2Slots(context,keymap,(dispose)=>{onDispose(dispose)
7193
7337
  })
7194
- setHostCapabilities(api,{todo:true,lsp:false,titleVisibility:false,unavailable:{todo:"OpenCode 2 does not expose Todo data through its supported plugin API."
7195
- }})
7338
+ setHostCapabilities(api,{todo:true,lsp:false,titleVisibility:false
7339
+ })
7196
7340
  return{api,async dispose(){controller.abort()
7197
7341
  for(let index=disposers.length-1;index>=0; index--)
7198
7342
  await disposers[index]()
@@ -7264,9 +7408,9 @@ if(item.source.target.startsWith("."))
7264
7408
  throw new Error("Unrecognized local Navigator source")
7265
7409
  await context.client.plugin.update({location:response.location,targets:[item.source.target]})
7266
7410
  }}}
7267
- async function setupNavigator(api,options,readProject){const config=pluginConfig(undefined)
7411
+ async function setupNavigator(api,options,readProject,managedTodo){const config=pluginConfig(undefined)
7268
7412
  const configured=await loadConfiguredDefaults(api,options,readProject)
7269
- const todo=createTodoController(api)
7413
+ const todo=managedTodo??createTodoController(api)
7270
7414
  const subagents=createSubagentController(api)
7271
7415
  const skills=createSkillController(api)
7272
7416
  const preferences=createPreferencesController(api,config,createPreferencesStore(api.state.path.state),configured)
@@ -7293,7 +7437,7 @@ api.slots.register({order:100,slots:{app(){return _$createComponent(IconProvider
7293
7437
  },interaction,skills,subagents,mcp
7294
7438
  }),_$createComponent(FirstRunWizardPersistence,{api,preferences
7295
7439
  }),_$createComponent(StartupSessionBinding,{api,controller:startup
7296
- }),_$createComponent(UpgradeNotesBinding,{api,preferences,version:"0.17.3"
7440
+ }),_$createComponent(UpgradeNotesBinding,{api,preferences,version:"0.18.0"
7297
7441
  }),_$createComponent(McpPersistence,{api,controller:mcp
7298
7442
  })]
7299
7443
  }})
@@ -7323,33 +7467,34 @@ throw result.error
7323
7467
  }
7324
7468
  return result.data?.type==="text"?result.data.content:undefined
7325
7469
  })
7326
- const versionStatus=createVersionStatus(api,"0.17.3")
7327
- const versionUpdates=createVersionUpdateActions(api,versionStatus,createOpenCodeV1VersionUpdater(api,meta),()=>preferences.rememberNavigatorVersionBeforeUpdate("0.17.3"))
7470
+ const versionStatus=createVersionStatus(api,"0.18.0")
7471
+ const versionUpdates=createVersionUpdateActions(api,versionStatus,createOpenCodeV1VersionUpdater(api,meta),()=>preferences.rememberNavigatorVersionBeforeUpdate("0.18.0"))
7328
7472
  api.slots.register({order:90,slots:{sidebar_footer(_context,props){return _$createComponent(IconProvider,{get style(){return preferences.lspIconStyle
7329
7473
  },get multilineCorners(){return preferences.cornerFont
7330
7474
  },get children(){return _$createComponent(VersionFooter,{api,get sessionID(){return props.session_id
7331
- },navigatorVersion:"0.17.3",status:versionStatus,get onOpenCodeUpdate(){return versionUpdates.openCode
7475
+ },navigatorVersion:"0.18.0",status:versionStatus,get onOpenCodeUpdate(){return versionUpdates.openCode
7332
7476
  },get onNavigatorUpdate(){return versionUpdates.navigator
7333
7477
  }})
7334
7478
  }})
7335
7479
  }}})
7336
7480
  }
7337
7481
  var setupOpenCodeV2=async (context)=>{const adapter=createOpenCodeV2Api(context)
7482
+ const todo=createNavigatorTodoController(context,adapter.api)
7338
7483
  const{mcp,preferences
7339
7484
  }=await setupNavigator(adapter.api,context.options,async ()=>{if(!context.client.file?.read)
7340
7485
  return
7341
7486
  return new TextDecoder().decode(await context.client.file.read({location:context.location,path:".opencode/navigator.json"
7342
7487
  }))
7343
- })
7344
- const versionStatus=createVersionStatus(adapter.api,"0.17.3")
7345
- const versionUpdates=createVersionUpdateActions(adapter.api,versionStatus,createOpenCodeV2VersionUpdater(context),()=>preferences.rememberNavigatorVersionBeforeUpdate("0.17.3"))
7488
+ },todo)
7489
+ const versionStatus=createVersionStatus(adapter.api,"0.18.0")
7490
+ const versionUpdates=createVersionUpdateActions(adapter.api,versionStatus,createOpenCodeV2VersionUpdater(context),()=>preferences.rememberNavigatorVersionBeforeUpdate("0.18.0"))
7346
7491
  const disposeFooter=context.ui.slot({append:"sidebar.footer",render:()=>_$createComponent(IconProvider,{get style(){return preferences.lspIconStyle
7347
7492
  },get multilineCorners(){return preferences.cornerFont
7348
7493
  },get children(){var _el$=_$createElement("box")
7349
7494
  _$setProp(_el$,"flexDirection","row")
7350
7495
  _$setProp(_el$,"justifyContent","space-between")
7351
7496
  _$insert(_el$,_$createComponent(VersionSummary,{get api(){return adapter.api
7352
- },navigatorVersion:"0.17.3",status:versionStatus,get onOpenCodeUpdate(){return versionUpdates.openCode
7497
+ },navigatorVersion:"0.18.0",status:versionStatus,get onOpenCodeUpdate(){return versionUpdates.openCode
7353
7498
  },get onNavigatorUpdate(){return versionUpdates.navigator
7354
7499
  }}),null)
7355
7500
  _$insert(_el$,_$createComponent(SettingsFooterButton,{get api(){return adapter.api
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package.json",
3
3
  "name": "opencode-navigator",
4
- "version": "0.17.3",
4
+ "version": "0.18.0",
5
5
  "description": "Search, navigate, and manage OpenCode sessions from the TUI",
6
6
  "type": "module",
7
7
  "packageManager": "bun@1.4.2",
@@ -27,6 +27,8 @@
27
27
  ],
28
28
  "files": [
29
29
  "dist",
30
+ "server.js",
31
+ "rpc.js",
30
32
  "assets",
31
33
  "scripts/install-font.mjs",
32
34
  "scripts/update-local-snapshot.mjs",
@@ -40,6 +42,12 @@
40
42
  "exports": {
41
43
  "./tui": {
42
44
  "import": "./dist/tui.js"
45
+ },
46
+ "./server": {
47
+ "import": "./dist/server.js"
48
+ },
49
+ "./rpc": {
50
+ "import": "./dist/rpc.js"
43
51
  }
44
52
  },
45
53
  "publishConfig": {
package/rpc.js ADDED
@@ -0,0 +1 @@
1
+ export { NavigatorTodoRpc } from './dist/rpc.js'
package/server.js ADDED
@@ -0,0 +1 @@
1
+ export { default } from './dist/server.js'