11const fs = require ( 'fs' ) . promises ;
22const fsSync = require ( 'fs' ) ;
33const path = require ( 'path' ) ;
4+ const os = require ( 'os' ) ;
45const { exec, spawn } = require ( 'child_process' ) ;
56const util = require ( 'util' ) ;
67const winston = require ( 'winston' ) ;
@@ -20,29 +21,48 @@ const logger = winston.createLogger({
2021} ) ;
2122
2223// Project categories - determines folder structure
24+ function expandUserPath ( p ) {
25+ const raw = String ( p || '' ) . trim ( ) ;
26+ if ( ! raw ) return raw ;
27+
28+ if ( raw === '~' ) return os . homedir ( ) ;
29+ if ( raw . startsWith ( '~/' ) ) return path . join ( os . homedir ( ) , raw . slice ( 2 ) ) ;
30+ return raw ;
31+ }
32+
33+ function resolveGitHubRoot ( ) {
34+ const envRoot = process . env . GREENFIELD_GITHUB_ROOT || process . env . GITHUB_ROOT || '' ;
35+ const root = envRoot ? expandUserPath ( envRoot ) : path . join ( os . homedir ( ) , 'GitHub' ) ;
36+ return root ;
37+ }
38+
39+ function joinGitHubRoot ( ...parts ) {
40+ return path . join ( resolveGitHubRoot ( ) , ...parts ) ;
41+ }
42+
2343const PROJECT_CATEGORIES = {
2444 'website' : {
25- path : '~/GitHub/ websites',
45+ path : joinGitHubRoot ( ' websites') ,
2646 keywords : [ 'website' , 'web app' , 'frontend' , 'landing page' , 'portfolio' , 'blog' ]
2747 } ,
2848 'game' : {
29- path : '~/GitHub/ games',
49+ path : joinGitHubRoot ( ' games') ,
3050 keywords : [ 'game' , 'hytopia' , 'unity' , 'godot' , 'gaming' ]
3151 } ,
3252 'tool' : {
33- path : '~/GitHub/ tools',
53+ path : joinGitHubRoot ( ' tools') ,
3454 keywords : [ 'tool' , 'cli' , 'utility' , 'automation' , 'script' ]
3555 } ,
3656 'api' : {
37- path : '~/GitHub/ apis',
57+ path : joinGitHubRoot ( ' apis') ,
3858 keywords : [ 'api' , 'backend' , 'server' , 'service' , 'rest' , 'graphql' ]
3959 } ,
4060 'library' : {
41- path : '~/GitHub/ libraries',
61+ path : joinGitHubRoot ( ' libraries') ,
4262 keywords : [ 'library' , 'package' , 'module' , 'npm' , 'sdk' ]
4363 } ,
4464 'other' : {
45- path : '~/GitHub/ projects',
65+ path : joinGitHubRoot ( ' projects') ,
4666 keywords : [ ]
4767 }
4868} ;
@@ -54,7 +74,7 @@ const PROJECT_TEMPLATES = {
5474 description : 'Hytopia SDK game project' ,
5575 initCommand : 'npx create-hytopia@latest' ,
5676 postInit : [ ] ,
57- defaultPath : '~/GitHub/ games/ hytopia/ games'
77+ defaultPath : joinGitHubRoot ( ' games' , ' hytopia' , ' games')
5878 } ,
5979 'node-typescript' : {
6080 name : 'Node.js TypeScript' ,
93113.env
94114*.log`
95115 } ,
96- defaultPath : '~/GitHub/ tools'
116+ defaultPath : joinGitHubRoot ( ' tools')
97117 } ,
98118 'empty' : {
99119 name : 'Empty Project' ,
@@ -103,7 +123,7 @@ dist/
103123 'README.md' : `# {{projectName}}\n\nProject description here.` ,
104124 '.gitignore' : `node_modules/\n.env\n*.log`
105125 } ,
106- defaultPath : '~/GitHub'
126+ defaultPath : resolveGitHubRoot ( )
107127 }
108128} ;
109129
@@ -252,7 +272,7 @@ class GreenfieldService {
252272 // Detect or use provided category
253273 const category = providedCategory || this . detectCategory ( description ) ;
254274 const categoryConfig = this . categories [ category ] ;
255- const basePath = categoryConfig . path . replace ( '~' , process . env . HOME ) ;
275+ const basePath = expandUserPath ( categoryConfig . path ) ;
256276
257277 logger . info ( 'Detected category' , { category, basePath } ) ;
258278
@@ -663,7 +683,7 @@ Start by understanding the requirements, then design and implement the solution.
663683 * Validate a project path
664684 */
665685 async validatePath ( projectPath ) {
666- const expandedPath = projectPath . replace ( '~' , process . env . HOME ) ;
686+ const expandedPath = expandUserPath ( projectPath ) ;
667687
668688 try {
669689 const stats = await fs . stat ( expandedPath ) ;
0 commit comments