@@ -6,7 +6,21 @@ import { listScripts } from "./discovery";
66function createMockRuntime ( responses : Map < string , { stdout : string ; exitCode : number } > ) : Runtime {
77 const runtime : Runtime = {
88 exec : ( command : string ) => {
9- const response = responses . get ( command ) ?? { stdout : "" , exitCode : 1 } ;
9+ // Check for exact match first
10+ let response = responses . get ( command ) ;
11+
12+ // Fallback: check if any key is a substring of the command
13+ if ( ! response ) {
14+ for ( const [ key , val ] of responses . entries ( ) ) {
15+ if ( command . includes ( key ) ) {
16+ response = val ;
17+ break ;
18+ }
19+ }
20+ }
21+
22+ response = response ?? { stdout : "" , exitCode : 1 } ;
23+
1024 return Promise . resolve ( {
1125 stdout : new ReadableStream ( {
1226 start ( controller ) {
@@ -62,50 +76,47 @@ function createMockRuntime(responses: Map<string, { stdout: string; exitCode: nu
6276}
6377
6478describe ( "listScripts" , ( ) => {
79+ const separator = ":::MUX_SCRIPT_START:::" ;
80+
6581 test ( "returns empty array when scripts directory doesn't exist" , async ( ) => {
6682 const runtime = createMockRuntime (
6783 new Map ( [
6884 [
69- `find "/test/workspace/.cmux/scripts" -maxdepth 1 -type f -print 2>/dev/null | while read f; do basename "$f"; done | sort || true` ,
85+ separator , // Match the unique separator in the command
7086 { stdout : "" , exitCode : 1 } ,
7187 ] ,
7288 ] )
7389 ) ;
7490
75- const scripts = await listScripts ( runtime , "/test/workspace" ) ;
91+ const scripts = await listScripts ( runtime , "/test/workspace/empty " ) ;
7692 expect ( scripts ) . toEqual ( [ ] ) ;
7793 } ) ;
7894
7995 test ( "discovers scripts with descriptions" , async ( ) => {
96+ const output = [
97+ `${ separator } deploy` ,
98+ "IS_EXECUTABLE:1" ,
99+ "#!/bin/bash" ,
100+ "# Description: Deploy the application" ,
101+ "echo 'deploying...'" ,
102+ "" ,
103+ `${ separator } test.sh` ,
104+ "IS_EXECUTABLE:0" ,
105+ "#!/bin/bash" ,
106+ "# Run tests" ,
107+ "echo 'testing...'" ,
108+ ] . join ( "\n" ) ;
109+
80110 const runtime = createMockRuntime (
81111 new Map ( [
82112 [
83- `find "/test/workspace/.cmux/scripts" -maxdepth 1 -type f -print 2>/dev/null | while read f; do basename "$f"; done | sort || true` ,
84- { stdout : "deploy\ntest.sh\n" , exitCode : 0 } ,
85- ] ,
86- [
87- 'test -x "/test/workspace/.cmux/scripts/deploy" && echo "true" || echo "false"' ,
88- { stdout : "true\n" , exitCode : 0 } ,
89- ] ,
90- [
91- 'head -n 20 "/test/workspace/.cmux/scripts/deploy" 2>/dev/null || true' ,
92- {
93- stdout : "#!/bin/bash\n# Description: Deploy the application\necho 'deploying...'\n" ,
94- exitCode : 0 ,
95- } ,
96- ] ,
97- [
98- 'test -x "/test/workspace/.cmux/scripts/test.sh" && echo "true" || echo "false"' ,
99- { stdout : "false\n" , exitCode : 0 } ,
100- ] ,
101- [
102- 'head -n 20 "/test/workspace/.cmux/scripts/test.sh" 2>/dev/null || true' ,
103- { stdout : "#!/bin/bash\n# Run tests\necho 'testing...'\n" , exitCode : 0 } ,
113+ separator ,
114+ { stdout : output , exitCode : 0 } ,
104115 ] ,
105116 ] )
106117 ) ;
107118
108- const scripts = await listScripts ( runtime , "/test/workspace" ) ;
119+ const scripts = await listScripts ( runtime , "/test/workspace/desc " ) ;
109120 expect ( scripts ) . toEqual ( [
110121 {
111122 name : "deploy" ,
@@ -121,27 +132,24 @@ describe("listScripts", () => {
121132 } ) ;
122133
123134 test ( "handles scripts with @description annotation" , async ( ) => {
135+ const output = [
136+ `${ separator } build` ,
137+ "IS_EXECUTABLE:1" ,
138+ "#!/bin/bash" ,
139+ "# @description Build the project" ,
140+ "echo 'building...'" ,
141+ ] . join ( "\n" ) ;
142+
124143 const runtime = createMockRuntime (
125144 new Map ( [
126145 [
127- `find "/test/workspace/.cmux/scripts" -maxdepth 1 -type f -print 2>/dev/null | while read f; do basename "$f"; done | sort || true` ,
128- { stdout : "build\n" , exitCode : 0 } ,
129- ] ,
130- [
131- 'test -x "/test/workspace/.cmux/scripts/build" && echo "true" || echo "false"' ,
132- { stdout : "true\n" , exitCode : 0 } ,
133- ] ,
134- [
135- 'head -n 20 "/test/workspace/.cmux/scripts/build" 2>/dev/null || true' ,
136- {
137- stdout : "#!/bin/bash\n# @description Build the project\necho 'building...'\n" ,
138- exitCode : 0 ,
139- } ,
146+ separator ,
147+ { stdout : output , exitCode : 0 } ,
140148 ] ,
141149 ] )
142150 ) ;
143151
144- const scripts = await listScripts ( runtime , "/test/workspace" ) ;
152+ const scripts = await listScripts ( runtime , "/test/workspace/annotation " ) ;
145153 expect ( scripts ) . toEqual ( [
146154 {
147155 name : "build" ,
@@ -152,38 +160,28 @@ describe("listScripts", () => {
152160 } ) ;
153161
154162 test ( "handles descriptions with various case and indentation" , async ( ) => {
163+ const output = [
164+ `${ separator } case-test` ,
165+ "IS_EXECUTABLE:1" ,
166+ "#!/bin/bash" ,
167+ "# description: Lowercase description" ,
168+ "" ,
169+ `${ separator } indent-test` ,
170+ "IS_EXECUTABLE:1" ,
171+ "#!/bin/bash" ,
172+ " # Description: Indented description" ,
173+ ] . join ( "\n" ) ;
174+
155175 const runtime = createMockRuntime (
156176 new Map ( [
157177 [
158- `find "/test/workspace/.cmux/scripts" -maxdepth 1 -type f -print 2>/dev/null | while read f; do basename "$f"; done | sort || true` ,
159- { stdout : "case-test\nindent-test\n" , exitCode : 0 } ,
160- ] ,
161- [
162- 'test -x "/test/workspace/.cmux/scripts/case-test" && echo "true" || echo "false"' ,
163- { stdout : "true\n" , exitCode : 0 } ,
164- ] ,
165- [
166- 'head -n 20 "/test/workspace/.cmux/scripts/case-test" 2>/dev/null || true' ,
167- {
168- stdout : "#!/bin/bash\n# description: Lowercase description\n" ,
169- exitCode : 0 ,
170- } ,
171- ] ,
172- [
173- 'test -x "/test/workspace/.cmux/scripts/indent-test" && echo "true" || echo "false"' ,
174- { stdout : "true\n" , exitCode : 0 } ,
175- ] ,
176- [
177- 'head -n 20 "/test/workspace/.cmux/scripts/indent-test" 2>/dev/null || true' ,
178- {
179- stdout : "#!/bin/bash\n # Description: Indented description\n" ,
180- exitCode : 0 ,
181- } ,
178+ separator ,
179+ { stdout : output , exitCode : 0 } ,
182180 ] ,
183181 ] )
184182 ) ;
185183
186- const scripts = await listScripts ( runtime , "/test/workspace" ) ;
184+ const scripts = await listScripts ( runtime , "/test/workspace/case " ) ;
187185 expect ( scripts ) . toEqual ( [
188186 {
189187 name : "case-test" ,
@@ -199,27 +197,23 @@ describe("listScripts", () => {
199197 } ) ;
200198
201199 test ( "handles tool-style descriptions with indentation" , async ( ) => {
200+ const output = [
201+ `${ separator } tool-indent` ,
202+ "IS_EXECUTABLE:1" ,
203+ "#!/bin/bash" ,
204+ " # @description Indented tool description" ,
205+ ] . join ( "\n" ) ;
206+
202207 const runtime = createMockRuntime (
203208 new Map ( [
204209 [
205- `find "/test/workspace/.cmux/scripts" -maxdepth 1 -type f -print 2>/dev/null | while read f; do basename "$f"; done | sort || true` ,
206- { stdout : "tool-indent\n" , exitCode : 0 } ,
207- ] ,
208- [
209- 'test -x "/test/workspace/.cmux/scripts/tool-indent" && echo "true" || echo "false"' ,
210- { stdout : "true\n" , exitCode : 0 } ,
211- ] ,
212- [
213- 'head -n 20 "/test/workspace/.cmux/scripts/tool-indent" 2>/dev/null || true' ,
214- {
215- stdout : "#!/bin/bash\n # @description Indented tool description\n" ,
216- exitCode : 0 ,
217- } ,
210+ separator ,
211+ { stdout : output , exitCode : 0 } ,
218212 ] ,
219213 ] )
220214 ) ;
221215
222- const scripts = await listScripts ( runtime , "/test/workspace" ) ;
216+ const scripts = await listScripts ( runtime , "/test/workspace/tool " ) ;
223217 expect ( scripts ) . toEqual ( [
224218 {
225219 name : "tool-indent" ,
@@ -230,24 +224,23 @@ describe("listScripts", () => {
230224 } ) ;
231225
232226 test ( "handles scripts without descriptions" , async ( ) => {
227+ const output = [
228+ `${ separator } script` ,
229+ "IS_EXECUTABLE:1" ,
230+ "#!/bin/bash" ,
231+ "echo 'no description'" ,
232+ ] . join ( "\n" ) ;
233+
233234 const runtime = createMockRuntime (
234235 new Map ( [
235236 [
236- `find "/test/workspace/.cmux/scripts" -maxdepth 1 -type f -print 2>/dev/null | while read f; do basename "$f"; done | sort || true` ,
237- { stdout : "script\n" , exitCode : 0 } ,
238- ] ,
239- [
240- 'test -x "/test/workspace/.cmux/scripts/script" && echo "true" || echo "false"' ,
241- { stdout : "true\n" , exitCode : 0 } ,
242- ] ,
243- [
244- 'head -n 20 "/test/workspace/.cmux/scripts/script" 2>/dev/null || true' ,
245- { stdout : "#!/bin/bash\necho 'no description'\n" , exitCode : 0 } ,
237+ separator ,
238+ { stdout : output , exitCode : 0 } ,
246239 ] ,
247240 ] )
248241 ) ;
249242
250- const scripts = await listScripts ( runtime , "/test/workspace" ) ;
243+ const scripts = await listScripts ( runtime , "/test/workspace/nodesc " ) ;
251244 expect ( scripts ) . toEqual ( [
252245 {
253246 name : "script" ,
0 commit comments