@@ -116,6 +116,7 @@ async function createAutomatorBmadFixture() {
116116 path . join ( skillDir , 'SKILL.md' ) ,
117117 [ '---' , `name: ${ skillName } ` , 'description: Automator skill' , '---' , '' , 'Automator body.' ] . join ( '\n' ) ,
118118 ) ;
119+ await fs . writeFile ( path . join ( skillDir , 'workflow.md' ) , `# ${ skillName } \n\nAutomator workflow body.\n` ) ;
119120 }
120121
121122 return fixtureDir ;
@@ -2826,6 +2827,28 @@ async function runTests() {
28262827 automatorInfo42 . installTargets . length === 1 && automatorInfo42 . installTargets . includes ( 'claude-code' ) ,
28272828 'BMad Automator is limited to Claude Code skill installation' ,
28282829 ) ;
2830+ const normalizedInfo42 = externalManager42 . _normalizeModule ( {
2831+ name : 'bad-shapes' ,
2832+ code : 'bad' ,
2833+ repository : 'https://example.com/bad.git' ,
2834+ install_targets : 'claude-code' ,
2835+ worker_targets : { bad : true } ,
2836+ requirements : [ 'tmux' , { bad : true } , false ] ,
2837+ } ) ;
2838+ assert (
2839+ Array . isArray ( normalizedInfo42 . installTargets ) && normalizedInfo42 . installTargets . includes ( 'claude-code' ) ,
2840+ 'External module install targets normalize scalar values to arrays' ,
2841+ ) ;
2842+ assert (
2843+ Array . isArray ( normalizedInfo42 . workerTargets ) && normalizedInfo42 . workerTargets . length === 0 ,
2844+ 'External module worker targets drop invalid shapes' ,
2845+ ) ;
2846+ assert (
2847+ normalizedInfo42 . requirements . length === 2 &&
2848+ normalizedInfo42 . requirements . includes ( 'tmux' ) &&
2849+ normalizedInfo42 . requirements . includes ( 'false' ) ,
2850+ 'External module requirements normalize scalar array entries' ,
2851+ ) ;
28292852
28302853 tempProjectDir42 = await fs . mkdtemp ( path . join ( os . tmpdir ( ) , 'bmad-automator-target-' ) ) ;
28312854 installedBmadDir42 = await createAutomatorBmadFixture ( ) ;
@@ -2846,6 +2869,30 @@ async function runTests() {
28462869 ! ( await fs . pathExists ( path . join ( tempProjectDir42 , '.agents' , 'skills' , 'bmad-story-automator' , 'SKILL.md' ) ) ) ,
28472870 'Codex setup skips Claude Code-only automator skill' ,
28482871 ) ;
2872+ assert (
2873+ ! ( await fs . pathExists ( path . join ( tempProjectDir42 , '.agents' , 'skills' , 'bmad-story-automator-review' , 'SKILL.md' ) ) ) ,
2874+ 'Codex setup skips Claude Code-only automator review skill' ,
2875+ ) ;
2876+
2877+ const escapeRoot42 = await fs . mkdtemp ( path . join ( os . tmpdir ( ) , 'bmad-source-root-' ) ) ;
2878+ const escapeRepo42 = path . join ( escapeRoot42 , 'repo' ) ;
2879+ await fs . ensureDir ( escapeRepo42 ) ;
2880+ const escapeManager42 = new ExternalModuleManager ( ) ;
2881+ escapeManager42 . getModuleByCode = async ( ) => ( {
2882+ code : 'escape' ,
2883+ builtIn : false ,
2884+ sourceRoot : '../outside' ,
2885+ } ) ;
2886+ escapeManager42 . cloneExternalModule = async ( ) => escapeRepo42 ;
2887+ let rejectedEscapingSourceRoot42 = false ;
2888+ try {
2889+ await escapeManager42 . findExternalModuleSource ( 'escape' ) ;
2890+ } catch ( error ) {
2891+ rejectedEscapingSourceRoot42 = error . message . includes ( 'source-root escapes repository' ) ;
2892+ } finally {
2893+ await fs . remove ( escapeRoot42 ) . catch ( ( ) => { } ) ;
2894+ }
2895+ assert ( rejectedEscapingSourceRoot42 , 'External module source-root cannot escape cloned repository' ) ;
28492896
28502897 const claudeResult42 = await ideManager42 . setup ( 'claude-code' , tempProjectDir42 , installedBmadDir42 , {
28512898 silent : true ,
@@ -2860,8 +2907,12 @@ async function runTests() {
28602907 await fs . pathExists ( path . join ( tempProjectDir42 , '.claude' , 'skills' , 'bmad-story-automator-review' , 'SKILL.md' ) ) ,
28612908 'Claude Code setup installs automator review skill' ,
28622909 ) ;
2910+ assert (
2911+ await fs . pathExists ( path . join ( tempProjectDir42 , '.claude' , 'skills' , 'bmad-story-automator-review' , 'workflow.md' ) ) ,
2912+ 'Claude Code setup copies automator workflow files' ,
2913+ ) ;
28632914 } catch ( error ) {
2864- assert ( false , ' Automator external skill-only module test succeeds' , error . message ) ;
2915+ assert ( false , ` Automator external skill-only module test succeeds: ${ error . message } ` ) ;
28652916 } finally {
28662917 if ( tempProjectDir42 ) await fs . remove ( tempProjectDir42 ) . catch ( ( ) => { } ) ;
28672918 if ( installedBmadDir42 ) await fs . remove ( path . dirname ( installedBmadDir42 ) ) . catch ( ( ) => { } ) ;
0 commit comments