1- import { cloneRepoAndCheckoutLatestTag , cloneRepo , createAIInstructions } from './init.js'
1+ import { cloneRepoAndCheckoutLatestTag , cloneRepo , createAIInstructions , createAIInstructionFiles } from './init.js'
22import { describe , expect , vi , test , beforeEach } from 'vitest'
33import { downloadGitRepository , removeGitRemote } from '@shopify/cli-kit/node/git'
4- import { rmdir , fileExists , copyDirectoryContents } from '@shopify/cli-kit/node/fs'
4+ import { rmdir , fileExists , readFile , writeFile , symlink } from '@shopify/cli-kit/node/fs'
55import { joinPath } from '@shopify/cli-kit/node/path'
66
77vi . mock ( '@shopify/cli-kit/node/git' )
@@ -11,7 +11,9 @@ vi.mock('@shopify/cli-kit/node/fs', async () => {
1111 ...actual ,
1212 fileExists : vi . fn ( ) ,
1313 rmdir : vi . fn ( ) ,
14- copyDirectoryContents : vi . fn ( ) ,
14+ readFile : vi . fn ( ) ,
15+ writeFile : vi . fn ( ) ,
16+ symlink : vi . fn ( ) ,
1517 inTemporaryDirectory : vi . fn ( async ( callback ) => {
1618 // eslint-disable-next-line node/no-callback-literal
1719 return callback ( '/tmp' )
@@ -151,26 +153,102 @@ describe('createAIInstructions()', () => {
151153
152154 beforeEach ( ( ) => {
153155 vi . mocked ( joinPath ) . mockImplementation ( ( ...paths ) => paths . join ( '/' ) )
156+ vi . mocked ( readFile ) . mockResolvedValue ( 'Sample AI instructions content' as any )
157+ vi . mocked ( writeFile ) . mockResolvedValue ( )
158+ vi . mocked ( symlink ) . mockResolvedValue ( )
154159 } )
155160
156- test ( 'creates AI instructions if it exists ' , async ( ) => {
161+ test ( 'creates AI instructions for a single instruction type ' , async ( ) => {
157162 // Given
158163 vi . mocked ( downloadGitRepository ) . mockResolvedValue ( )
159- vi . mocked ( copyDirectoryContents ) . mockResolvedValue ( )
160164
161165 // When
162166 await createAIInstructions ( destination , 'cursor' )
163167
164168 // Then
165169 expect ( downloadGitRepository ) . toHaveBeenCalled ( )
166- expect ( copyDirectoryContents ) . toHaveBeenCalledWith ( '/tmp/ai/cursor' , '/path/to/theme/.cursor' )
170+ expect ( readFile ) . toHaveBeenCalledWith ( '/tmp/ai/github/copilot-instructions.md' )
171+ expect ( writeFile ) . toHaveBeenCalledWith ( '/path/to/theme/AGENTS.md' , expect . stringContaining ( '# AGENTS.md' ) )
172+ expect ( symlink ) . toHaveBeenCalledWith ( '/path/to/theme/AGENTS.md' , '/path/to/theme/.cursorrules' )
167173 } )
168174
169- test ( 'throws an error when the AI instructions directory does not exist ' , async ( ) => {
175+ test ( 'creates AI instructions for all instruction types when "all" is selected ' , async ( ) => {
170176 // Given
171177 vi . mocked ( downloadGitRepository ) . mockResolvedValue ( )
172- vi . mocked ( copyDirectoryContents ) . mockRejectedValue ( new Error ( 'Directory does not exist' ) )
178+
179+ // When
180+ await createAIInstructions ( destination , 'all' )
181+
182+ // Then
183+ expect ( downloadGitRepository ) . toHaveBeenCalled ( )
184+ // github, cursor, claude
185+ expect ( readFile ) . toHaveBeenCalledTimes ( 3 )
186+ expect ( writeFile ) . toHaveBeenCalledTimes ( 3 )
187+ expect ( symlink ) . toHaveBeenCalledTimes ( 3 )
188+ expect ( symlink ) . toHaveBeenCalledWith ( '/path/to/theme/AGENTS.md' , '/path/to/theme/copilot-instructions.md' )
189+ expect ( symlink ) . toHaveBeenCalledWith ( '/path/to/theme/AGENTS.md' , '/path/to/theme/.cursorrules' )
190+ expect ( symlink ) . toHaveBeenCalledWith ( '/path/to/theme/AGENTS.md' , '/path/to/theme/CLAUDE.md' )
191+ } )
192+
193+ test ( 'throws an error when file operations fail' , async ( ) => {
194+ // Given
195+ vi . mocked ( downloadGitRepository ) . mockResolvedValue ( )
196+ vi . mocked ( readFile ) . mockRejectedValue ( new Error ( 'File not found' ) )
173197
174198 await expect ( createAIInstructions ( destination , 'cursor' ) ) . rejects . toThrow ( 'Failed to create AI instructions' )
175199 } )
176200} )
201+
202+ describe ( 'createAIInstructionFiles()' , ( ) => {
203+ const tempDir = '/tmp'
204+ const themeRoot = '/path/to/theme'
205+
206+ beforeEach ( ( ) => {
207+ vi . mocked ( joinPath ) . mockImplementation ( ( ...paths ) => paths . join ( '/' ) )
208+ vi . mocked ( readFile ) . mockResolvedValue ( 'AI instruction content' as any )
209+ vi . mocked ( writeFile ) . mockResolvedValue ( )
210+ vi . mocked ( symlink ) . mockResolvedValue ( )
211+ } )
212+
213+ test ( 'creates AGENTS.md with prepended header for github instruction' , async ( ) => {
214+ // When
215+ await createAIInstructionFiles ( tempDir , themeRoot , 'github' )
216+
217+ // Then
218+ expect ( readFile ) . toHaveBeenCalledWith ( '/tmp/ai/github/copilot-instructions.md' )
219+ expect ( writeFile ) . toHaveBeenCalledWith ( '/path/to/theme/AGENTS.md' , '# AGENTS.md\n\nAI instruction content' )
220+ expect ( symlink ) . toHaveBeenCalledWith ( '/path/to/theme/AGENTS.md' , '/path/to/theme/copilot-instructions.md' )
221+ } )
222+
223+ test ( 'creates AGENTS.md and .cursorrules symlink for cursor instruction' , async ( ) => {
224+ // When
225+ await createAIInstructionFiles ( tempDir , themeRoot , 'cursor' )
226+
227+ // Then
228+ expect ( readFile ) . toHaveBeenCalledWith ( '/tmp/ai/github/copilot-instructions.md' )
229+ expect ( writeFile ) . toHaveBeenCalledWith ( '/path/to/theme/AGENTS.md' , '# AGENTS.md\n\nAI instruction content' )
230+ expect ( symlink ) . toHaveBeenCalledWith ( '/path/to/theme/AGENTS.md' , '/path/to/theme/.cursorrules' )
231+ } )
232+
233+ test ( 'creates AGENTS.md and CLAUDE.md symlink for claude instruction' , async ( ) => {
234+ // When
235+ await createAIInstructionFiles ( tempDir , themeRoot , 'claude' )
236+
237+ // Then
238+ expect ( readFile ) . toHaveBeenCalledWith ( '/tmp/ai/github/copilot-instructions.md' )
239+ expect ( writeFile ) . toHaveBeenCalledWith ( '/path/to/theme/AGENTS.md' , '# AGENTS.md\n\nAI instruction content' )
240+ expect ( symlink ) . toHaveBeenCalledWith ( '/path/to/theme/AGENTS.md' , '/path/to/theme/CLAUDE.md' )
241+ } )
242+
243+ test ( 'prepends header to source content' , async ( ) => {
244+ // Given
245+ const sourceContent = 'Original content from repo'
246+ vi . mocked ( readFile ) . mockResolvedValue ( sourceContent as any )
247+
248+ // When
249+ await createAIInstructionFiles ( tempDir , themeRoot , 'github' )
250+
251+ // Then
252+ expect ( writeFile ) . toHaveBeenCalledWith ( '/path/to/theme/AGENTS.md' , `# AGENTS.md\n\n${ sourceContent } ` )
253+ } )
254+ } )
0 commit comments