@@ -111,7 +111,7 @@ export function useHomeCommands(options: UseHomeCommandsOptions) {
111111 // Add import option at the top (no category)
112112 const selectOptions = [
113113 {
114- title : "⬇ Import template from GitHub" ,
114+ title : "⬇ Import template ( GitHub or local) " ,
115115 value : IMPORT_ACTION ,
116116 } ,
117117 ...templateOptions ,
@@ -322,7 +322,7 @@ export function useHomeCommands(options: UseHomeCommandsOptions) {
322322 isImportInstalled,
323323 ensureImportsDir,
324324 } = await import ( "../../../../../shared/imports/index.js" )
325- const { existsSync, rmSync } = await import ( "node:fs" )
325+ const { existsSync, rmSync, cpSync } = await import ( "node:fs" )
326326 const { spawn } = await import ( "node:child_process" )
327327
328328 const cloneRepo = ( url : string , destPath : string ) : Promise < void > => {
@@ -351,6 +351,10 @@ export function useHomeCommands(options: UseHomeCommandsOptions) {
351351 } )
352352 }
353353
354+ const copyLocalFolder = ( sourcePath : string , destPath : string ) : void => {
355+ cpSync ( sourcePath , destPath , { recursive : true } )
356+ }
357+
354358 const removeGitDir = ( repoPath : string ) : void => {
355359 const { join } = require ( "node:path" )
356360 const gitDir = join ( repoPath , ".git" )
@@ -361,7 +365,7 @@ export function useHomeCommands(options: UseHomeCommandsOptions) {
361365
362366 const performInstall = async ( source : string ) => {
363367 try {
364- // Resolve the source to a clone URL
368+ // Resolve the source to a clone URL or local path
365369 const resolved = await resolveSource ( source )
366370 const installPath = getImportInstallPath ( resolved . repoName )
367371
@@ -373,10 +377,15 @@ export function useHomeCommands(options: UseHomeCommandsOptions) {
373377 // Ensure imports directory exists
374378 ensureImportsDir ( )
375379
376- // Clone the repository
377- await cloneRepo ( resolved . url , installPath )
380+ // Handle local paths vs git URLs
381+ if ( resolved . type === "local-path" ) {
382+ copyLocalFolder ( resolved . url , installPath )
383+ } else {
384+ // Clone the repository
385+ await cloneRepo ( resolved . url , installPath )
386+ }
378387
379- // Remove .git directory
388+ // Remove .git directory (in case local folder had one)
380389 removeGitDir ( installPath )
381390
382391 // Validate the import
@@ -394,9 +403,11 @@ export function useHomeCommands(options: UseHomeCommandsOptions) {
394403 if ( hasMissingManifest ) {
395404 return {
396405 success : false ,
397- error : "Missing codemachine.json file" ,
406+ error : "Missing manifest file" ,
398407 errorDetails :
399- "The repository must contain a codemachine.json manifest file in the root directory." ,
408+ resolved . type === "local-path"
409+ ? "The folder must contain a .codemachine.json or codemachine.json manifest file."
410+ : "The repository must contain a codemachine.json manifest file in the root directory." ,
400411 }
401412 }
402413
@@ -446,6 +457,22 @@ export function useHomeCommands(options: UseHomeCommandsOptions) {
446457 }
447458 }
448459
460+ if ( errorMessage . includes ( "ENOENT" ) || errorMessage . includes ( "no such file" ) ) {
461+ return {
462+ success : false ,
463+ error : "Local folder not found" ,
464+ errorDetails : "Check that the path exists and is accessible." ,
465+ }
466+ }
467+
468+ if ( errorMessage . includes ( "EACCES" ) || errorMessage . includes ( "permission denied" ) ) {
469+ return {
470+ success : false ,
471+ error : "Permission denied" ,
472+ errorDetails : "Check that you have read access to the folder." ,
473+ }
474+ }
475+
449476 return {
450477 success : false ,
451478 error : errorMessage ,
0 commit comments