@@ -50,6 +50,24 @@ function cleanupDir(dir: string): void {
5050 }
5151}
5252
53+ /**
54+ * Create a mock ToolContext for testing.
55+ * Includes a no-op `ask` spy so permission checks in the plugin's `wrap()` work.
56+ */
57+ function createMockToolContext ( overrides : Record < string , unknown > = { } ) {
58+ return {
59+ sessionID : 'test-session' ,
60+ messageID : 'test-message' ,
61+ agent : 'workflow' ,
62+ directory : '' ,
63+ worktree : '' ,
64+ abort : new AbortController ( ) . signal ,
65+ metadata : vi . fn ( ) ,
66+ ask : vi . fn ( ) . mockResolvedValue ( undefined ) ,
67+ ...overrides ,
68+ } ;
69+ }
70+
5371/**
5472 * Create a mock PluginInput for testing
5573 */
@@ -571,7 +589,7 @@ describe('OpenCode Workflows Plugin E2E', () => {
571589
572590 const result = await hooks . tool ! . start_development . execute (
573591 { workflow : 'epcc' } ,
574- { } as never
592+ createMockToolContext ( )
575593 ) ;
576594
577595 // start_development returns instructions from handler
@@ -592,7 +610,7 @@ describe('OpenCode Workflows Plugin E2E', () => {
592610
593611 const result = await hooks . tool ! . start_development . execute (
594612 { workflow : 'waterfall' } ,
595- { } as never
613+ createMockToolContext ( )
596614 ) ;
597615
598616 // When trying to start a different workflow, we get an error about existing workflow
@@ -615,7 +633,7 @@ describe('OpenCode Workflows Plugin E2E', () => {
615633
616634 const result = await hooks . tool ! . proceed_to_phase . execute (
617635 { target_phase : 'plan' , reason : 'exploration complete' } ,
618- { } as never
636+ createMockToolContext ( )
619637 ) ;
620638
621639 // Transition output now shows the new phase clearly
@@ -645,7 +663,7 @@ describe('OpenCode Workflows Plugin E2E', () => {
645663 const sessionID = 'test-session-123' ;
646664 await hooks . tool ! . proceed_to_phase . execute (
647665 { target_phase : 'plan' , reason : 'exploration complete' } ,
648- { sessionID } as never
666+ createMockToolContext ( { sessionID } )
649667 ) ;
650668
651669 // session.summarize should have been called with the session ID and model
@@ -664,9 +682,10 @@ describe('OpenCode Workflows Plugin E2E', () => {
664682 hooks = await WorkflowsPlugin ( mockInput ) ;
665683
666684 const sessionID = 'test-session-456' ;
667- await hooks . tool ! . proceed_to_phase . execute ( { target_phase : 'plan' } , {
668- sessionID,
669- } as never ) ;
685+ await hooks . tool ! . proceed_to_phase . execute (
686+ { target_phase : 'plan' } ,
687+ createMockToolContext ( { sessionID } )
688+ ) ;
670689
671690 // session.summarize should NOT have been called — transition failed
672691 const summarizeMock = (
@@ -708,7 +727,7 @@ describe('OpenCode Workflows Plugin E2E', () => {
708727 const sessionID = 'test-session-789' ;
709728 const proceedResult = await hooks . tool ! . proceed_to_phase . execute (
710729 { target_phase : 'plan' , reason : 'exploration complete' } ,
711- { sessionID } as never
730+ createMockToolContext ( { sessionID } )
712731 ) ;
713732
714733 // Verify the transition itself succeeded before checking compaction was skipped
@@ -728,7 +747,7 @@ describe('OpenCode Workflows Plugin E2E', () => {
728747
729748 const result = await hooks . tool ! . proceed_to_phase . execute (
730749 { target_phase : 'plan' } ,
731- { } as never
750+ createMockToolContext ( )
732751 ) ;
733752
734753 // Error message from handler mentions "No development conversation" or similar
@@ -750,7 +769,7 @@ describe('OpenCode Workflows Plugin E2E', () => {
750769
751770 const result = await hooks . tool ! . reset_development . execute (
752771 { confirm : false } ,
753- { } as never
772+ createMockToolContext ( )
754773 ) ;
755774
756775 expect ( result ) . toContain ( 'confirm' ) ;
@@ -766,7 +785,7 @@ describe('OpenCode Workflows Plugin E2E', () => {
766785
767786 const result = await hooks . tool ! . reset_development . execute (
768787 { confirm : true , reason : 'testing reset' } ,
769- { } as never
788+ createMockToolContext ( )
770789 ) ;
771790
772791 // Reset message confirms deletion (may not include workflow name)
@@ -778,7 +797,7 @@ describe('OpenCode Workflows Plugin E2E', () => {
778797
779798 const result = await hooks . tool ! . reset_development . execute (
780799 { confirm : true } ,
781- { } as never
800+ createMockToolContext ( )
782801 ) ;
783802
784803 expect ( result ) . toContain ( 'No active workflow' ) ;
@@ -921,10 +940,10 @@ describe('WORKFLOW_AGENTS environment variable', () => {
921940
922941 // Tool should throw when agent is not in filter
923942 await expect (
924- hooks . tool ! [ 'start_development' ] . execute ( { workflow : 'minor' } , {
925- sessionID : 'test-session' ,
926- agent : 'explore' , // Not in filter
927- } as unknown )
943+ hooks . tool ! [ 'start_development' ] . execute (
944+ { workflow : 'minor' } ,
945+ createMockToolContext ( { agent : 'explore' } ) // Not in filter
946+ )
928947 ) . rejects . toThrow ( / n o t e n a b l e d f o r t h i s a g e n t / i) ;
929948 } finally {
930949 if ( originalEnv === undefined ) {
@@ -948,10 +967,10 @@ describe('WORKFLOW_AGENTS environment variable', () => {
948967 // (it may fail for other reasons like no plan file, but not the filter guard)
949968 let thrownMessage : string | undefined ;
950969 try {
951- await hooks . tool ! [ 'start_development' ] . execute ( { workflow : 'minor' } , {
952- sessionID : 'test-session' ,
953- agent : 'general' , // In filter
954- } as unknown ) ;
970+ await hooks . tool ! [ 'start_development' ] . execute (
971+ { workflow : 'minor' } ,
972+ createMockToolContext ( { agent : 'general' } ) // In filter
973+ ) ;
955974 } catch ( err ) {
956975 thrownMessage = ( err as Error ) . message ;
957976 }
@@ -989,10 +1008,10 @@ describe('WORKFLOW_AGENTS environment variable', () => {
9891008 // Tool should not throw agent filter error for any agent
9901009 let thrownMessage : string | undefined ;
9911010 try {
992- await hooks . tool ! [ 'start_development' ] . execute ( { workflow : 'minor' } , {
993- sessionID : 'test-session' ,
994- agent : 'any-agent' , // Should work when no filter is set
995- } as unknown ) ;
1011+ await hooks . tool ! [ 'start_development' ] . execute (
1012+ { workflow : 'minor' } ,
1013+ createMockToolContext ( { agent : 'any-agent' } ) // Should work when no filter is set
1014+ ) ;
9961015 } catch ( err ) {
9971016 thrownMessage = ( err as Error ) . message ;
9981017 }
@@ -1020,10 +1039,10 @@ describe('WORKFLOW_AGENTS environment variable', () => {
10201039 // Should parse correctly and allow the whitelisted agents
10211040 let thrownMessage : string | undefined ;
10221041 try {
1023- await hooks . tool ! [ 'start_development' ] . execute ( { workflow : 'minor' } , {
1024- sessionID : 'test-session' ,
1025- agent : 'architect' , // Should work after trimming
1026- } as unknown ) ;
1042+ await hooks . tool ! [ 'start_development' ] . execute (
1043+ { workflow : 'minor' } ,
1044+ createMockToolContext ( { agent : 'architect' } ) // Should work after trimming
1045+ ) ;
10271046 } catch ( err ) {
10281047 thrownMessage = ( err as Error ) . message ;
10291048 }
@@ -1033,10 +1052,10 @@ describe('WORKFLOW_AGENTS environment variable', () => {
10331052
10341053 // And reject agents not in the list
10351054 await expect (
1036- hooks . tool ! [ 'start_development' ] . execute ( { workflow : 'minor' } , {
1037- sessionID : 'test-session' ,
1038- agent : 'other-agent' , // Not in list
1039- } as unknown )
1055+ hooks . tool ! [ 'start_development' ] . execute (
1056+ { workflow : 'minor' } ,
1057+ createMockToolContext ( { agent : 'other-agent' } ) // Not in list
1058+ )
10401059 ) . rejects . toThrow ( / n o t e n a b l e d f o r t h i s a g e n t / i) ;
10411060 } finally {
10421061 if ( originalEnv === undefined ) {
0 commit comments