@@ -37,11 +37,13 @@ export class WorkspaceService {
3737 private workspacesRoot : string ;
3838 private sshKeysRoot : string ;
3939 private configRoot : string ;
40+ private templatesRoot : string ;
4041
41- constructor ( opts ?: { workspacesRoot ?: string ; sshKeysRoot ?: string ; configRoot ?: string } ) {
42+ constructor ( opts ?: { workspacesRoot ?: string ; sshKeysRoot ?: string ; configRoot ?: string ; templatesRoot ?: string } ) {
4243 this . workspacesRoot = opts ?. workspacesRoot || '/app/workspaces' ;
4344 this . sshKeysRoot = opts ?. sshKeysRoot || '/app/ssh-keys' ;
4445 this . configRoot = opts ?. configRoot || '/app/config' ;
46+ this . templatesRoot = opts ?. templatesRoot || '/app/templates' ;
4547 }
4648
4749 async createUserWorkspace ( userId : string ) : Promise < string > {
@@ -59,21 +61,18 @@ export class WorkspaceService {
5961 await this . storeSSHKey ( userId , opts . sshPrivateKey , opts . sshPublicKey ) ;
6062 }
6163
62- // 2. Deploy config-based skills ( from /app/config/claude/skills/)
63- await this . deploySkills ( userId ) ;
64+ // 2. Deploy global CLAUDE.md + skills from templates
65+ await this . deployGlobalTemplates ( userId ) ;
6466
65- // 3. Store custom API-provided skills (on top of config-based ones)
67+ // 3. Store custom API-provided skills (on top of template ones)
6668 if ( opts . skills ?. length ) {
6769 await this . storeSkills ( userId , opts . skills ) ;
6870 }
6971
70- // 4. Deploy global CLAUDE.md
71- await this . deployCLAUDEmd ( userId ) ;
72-
73- // 5. Store custom hooks (merged with notification hooks)
72+ // 4. Store custom hooks (merged with notification hooks)
7473 await this . storeHooks ( userId , undefined , undefined , opts . hooks ) ;
7574
76- // 6 . Store Claude settings
75+ // 5 . Store Claude settings
7776 if ( opts . claudeSettings ) {
7877 await this . storeSettings ( userId , opts . claudeSettings ) ;
7978 }
@@ -162,35 +161,25 @@ export class WorkspaceService {
162161 }
163162 }
164163
165- async deployCLAUDEmd ( userId : string ) : Promise < void > {
166- const sourcePath = join ( this . configRoot , 'claude' , 'CLAUDE.md' ) ;
167- const destPath = join ( this . workspacesRoot , userId , 'CLAUDE.md' ) ;
164+ async deployGlobalTemplates ( userId : string ) : Promise < void > {
165+ const claudeTemplateDir = join ( this . templatesRoot , 'claude' ) ;
166+ const userPath = join ( this . workspacesRoot , userId ) ;
167+
168+ // Deploy CLAUDE.md to workspace root
168169 try {
169- await copyFile ( sourcePath , destPath ) ;
170+ await copyFile ( join ( claudeTemplateDir , 'CLAUDE.md' ) , join ( userPath , 'CLAUDE.md' ) ) ;
170171 } catch {
171- // CLAUDE.md template not found, skip silently
172+ // CLAUDE.md template not found, skip
172173 }
173- }
174-
175- async deploySkills ( userId : string ) : Promise < void > {
176- const sourceDir = join ( this . configRoot , 'claude' , 'skills' ) ;
177- const destDir = join ( this . workspacesRoot , userId , '.claude' , 'skills' ) ;
178174
175+ // Deploy skills to .claude/skills/
179176 try {
180- const entries = await readdir ( sourceDir , { withFileTypes : true } ) ;
181- const skillDirs = entries . filter ( e => e . isDirectory ( ) && ! e . name . startsWith ( '.' ) ) ;
182-
183- if ( skillDirs . length === 0 ) return ;
184-
185- await mkdir ( destDir , { recursive : true } ) ;
186-
187- for ( const dir of skillDirs ) {
188- const src = join ( sourceDir , dir . name ) ;
189- const dest = join ( destDir , dir . name ) ;
190- await cp ( src , dest , { recursive : true , force : true } ) ;
191- }
177+ const skillsSource = join ( claudeTemplateDir , 'skills' ) ;
178+ const skillsDest = join ( userPath , '.claude' , 'skills' ) ;
179+ await readdir ( skillsSource ) ;
180+ await cp ( skillsSource , skillsDest , { recursive : true , force : true } ) ;
192181 } catch {
193- // Skills directory not found, skip silently
182+ // Skills directory not found, skip
194183 }
195184 }
196185
@@ -335,4 +324,5 @@ export const workspaceService = new WorkspaceService({
335324 workspacesRoot : process . env . WORKSPACES_ROOT || '/app/workspaces' ,
336325 sshKeysRoot : process . env . SSH_KEYS_ROOT || '/app/ssh-keys' ,
337326 configRoot : process . env . CONFIG_ROOT || '/app/config' ,
327+ templatesRoot : process . env . TEMPLATES_ROOT || '/app/templates' ,
338328} ) ;
0 commit comments