44 * SPDX-License-Identifier: Apache-2.0
55 */
66
7- import { access , cp , mkdir , readdir } from 'node:fs/promises' ;
8- import { join , dirname } from 'node:path' ;
7+ import { access , cp , mkdir , readdir , writeFile } from 'node:fs/promises' ;
8+ import { join , dirname , basename } from 'node:path' ;
99import type { CommandModule } from 'yargs' ;
1010import { fileURLToPath } from 'node:url' ;
1111import { getErrorMessage } from '../../utils/errors.js' ;
1212
1313interface NewArgs {
1414 path : string ;
15- template : string ;
15+ template ? : string ;
1616}
1717
1818const __filename = fileURLToPath ( import . meta. url ) ;
@@ -29,13 +29,17 @@ async function pathExists(path: string) {
2929 }
3030}
3131
32- async function copyDirectory ( template : string , path : string ) {
32+ async function createDirectory ( path : string ) {
3333 if ( await pathExists ( path ) ) {
3434 throw new Error ( `Path already exists: ${ path } ` ) ;
3535 }
36+ await mkdir ( path , { recursive : true } ) ;
37+ }
38+
39+ async function copyDirectory ( template : string , path : string ) {
40+ await createDirectory ( path ) ;
3641
3742 const examplePath = join ( EXAMPLES_PATH , template ) ;
38- await mkdir ( path , { recursive : true } ) ;
3943 const entries = await readdir ( examplePath , { withFileTypes : true } ) ;
4044 for ( const entry of entries ) {
4145 const srcPath = join ( examplePath , entry . name ) ;
@@ -46,10 +50,24 @@ async function copyDirectory(template: string, path: string) {
4650
4751async function handleNew ( args : NewArgs ) {
4852 try {
49- await copyDirectory ( args . template , args . path ) ;
50- console . log (
51- `Successfully created new extension from template "${ args . template } " at ${ args . path } .` ,
52- ) ;
53+ if ( args . template ) {
54+ await copyDirectory ( args . template , args . path ) ;
55+ console . log (
56+ `Successfully created new extension from template "${ args . template } " at ${ args . path } .` ,
57+ ) ;
58+ } else {
59+ await createDirectory ( args . path ) ;
60+ const extensionName = basename ( args . path ) ;
61+ const manifest = {
62+ name : extensionName ,
63+ version : '1.0.0' ,
64+ } ;
65+ await writeFile (
66+ join ( args . path , 'gemini-extension.json' ) ,
67+ JSON . stringify ( manifest , null , 2 ) ,
68+ ) ;
69+ console . log ( `Successfully created new extension at ${ args . path } .` ) ;
70+ }
5371 console . log (
5472 `You can install this using "gemini extensions link ${ args . path } " to test it out.` ,
5573 ) ;
@@ -67,7 +85,7 @@ async function getBoilerplateChoices() {
6785}
6886
6987export const newCommand : CommandModule = {
70- command : 'new <path> < template> ' ,
88+ command : 'new <path> [ template] ' ,
7189 describe : 'Create a new extension from a boilerplate example.' ,
7290 builder : async ( yargs ) => {
7391 const choices = await getBoilerplateChoices ( ) ;
@@ -85,7 +103,7 @@ export const newCommand: CommandModule = {
85103 handler : async ( args ) => {
86104 await handleNew ( {
87105 path : args [ 'path' ] as string ,
88- template : args [ 'template' ] as string ,
106+ template : args [ 'template' ] as string | undefined ,
89107 } ) ;
90108 } ,
91109} ;
0 commit comments