mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-03-13 23:51:16 -07:00
Update gemini extensions new (#10629)
This commit is contained in:
@@ -1,5 +1,4 @@
|
|||||||
{
|
{
|
||||||
"name": "context-example",
|
"name": "context-example",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0"
|
||||||
"contextFileName": "GEMINI.md"
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,15 +30,22 @@ describe('extensions new command', () => {
|
|||||||
it('should fail if no path is provided', async () => {
|
it('should fail if no path is provided', async () => {
|
||||||
const parser = yargs([]).command(newCommand).fail(false).locale('en');
|
const parser = yargs([]).command(newCommand).fail(false).locale('en');
|
||||||
await expect(parser.parseAsync('new')).rejects.toThrow(
|
await expect(parser.parseAsync('new')).rejects.toThrow(
|
||||||
'Not enough non-option arguments: got 0, need at least 2',
|
'Not enough non-option arguments: got 0, need at least 1',
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should fail if no template is provided', async () => {
|
it('should create directory when no template is provided', async () => {
|
||||||
const parser = yargs([]).command(newCommand).fail(false).locale('en');
|
mockedFs.access.mockRejectedValue(new Error('ENOENT'));
|
||||||
await expect(parser.parseAsync('new /some/path')).rejects.toThrow(
|
mockedFs.mkdir.mockResolvedValue(undefined);
|
||||||
'Not enough non-option arguments: got 1, need at least 2',
|
|
||||||
);
|
const parser = yargs([]).command(newCommand).fail(false);
|
||||||
|
|
||||||
|
await parser.parseAsync('new /some/path');
|
||||||
|
|
||||||
|
expect(mockedFs.mkdir).toHaveBeenCalledWith('/some/path', {
|
||||||
|
recursive: true,
|
||||||
|
});
|
||||||
|
expect(mockedFs.cp).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create directory and copy files when path does not exist', async () => {
|
it('should create directory and copy files when path does not exist', async () => {
|
||||||
|
|||||||
@@ -4,15 +4,15 @@
|
|||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { access, cp, mkdir, readdir } from 'node:fs/promises';
|
import { access, cp, mkdir, readdir, writeFile } from 'node:fs/promises';
|
||||||
import { join, dirname } from 'node:path';
|
import { join, dirname, basename } from 'node:path';
|
||||||
import type { CommandModule } from 'yargs';
|
import type { CommandModule } from 'yargs';
|
||||||
import { fileURLToPath } from 'node:url';
|
import { fileURLToPath } from 'node:url';
|
||||||
import { getErrorMessage } from '../../utils/errors.js';
|
import { getErrorMessage } from '../../utils/errors.js';
|
||||||
|
|
||||||
interface NewArgs {
|
interface NewArgs {
|
||||||
path: string;
|
path: string;
|
||||||
template: string;
|
template?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const __filename = fileURLToPath(import.meta.url);
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
@@ -29,13 +29,17 @@ async function pathExists(path: string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function copyDirectory(template: string, path: string) {
|
async function createDirectory(path: string) {
|
||||||
if (await pathExists(path)) {
|
if (await pathExists(path)) {
|
||||||
throw new Error(`Path already exists: ${path}`);
|
throw new Error(`Path already exists: ${path}`);
|
||||||
}
|
}
|
||||||
|
await mkdir(path, { recursive: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
async function copyDirectory(template: string, path: string) {
|
||||||
|
await createDirectory(path);
|
||||||
|
|
||||||
const examplePath = join(EXAMPLES_PATH, template);
|
const examplePath = join(EXAMPLES_PATH, template);
|
||||||
await mkdir(path, { recursive: true });
|
|
||||||
const entries = await readdir(examplePath, { withFileTypes: true });
|
const entries = await readdir(examplePath, { withFileTypes: true });
|
||||||
for (const entry of entries) {
|
for (const entry of entries) {
|
||||||
const srcPath = join(examplePath, entry.name);
|
const srcPath = join(examplePath, entry.name);
|
||||||
@@ -46,10 +50,24 @@ async function copyDirectory(template: string, path: string) {
|
|||||||
|
|
||||||
async function handleNew(args: NewArgs) {
|
async function handleNew(args: NewArgs) {
|
||||||
try {
|
try {
|
||||||
await copyDirectory(args.template, args.path);
|
if (args.template) {
|
||||||
console.log(
|
await copyDirectory(args.template, args.path);
|
||||||
`Successfully created new extension from template "${args.template}" at ${args.path}.`,
|
console.log(
|
||||||
);
|
`Successfully created new extension from template "${args.template}" at ${args.path}.`,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
await createDirectory(args.path);
|
||||||
|
const extensionName = basename(args.path);
|
||||||
|
const manifest = {
|
||||||
|
name: extensionName,
|
||||||
|
version: '1.0.0',
|
||||||
|
};
|
||||||
|
await writeFile(
|
||||||
|
join(args.path, 'gemini-extension.json'),
|
||||||
|
JSON.stringify(manifest, null, 2),
|
||||||
|
);
|
||||||
|
console.log(`Successfully created new extension at ${args.path}.`);
|
||||||
|
}
|
||||||
console.log(
|
console.log(
|
||||||
`You can install this using "gemini extensions link ${args.path}" to test it out.`,
|
`You can install this using "gemini extensions link ${args.path}" to test it out.`,
|
||||||
);
|
);
|
||||||
@@ -67,7 +85,7 @@ async function getBoilerplateChoices() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const newCommand: CommandModule = {
|
export const newCommand: CommandModule = {
|
||||||
command: 'new <path> <template>',
|
command: 'new <path> [template]',
|
||||||
describe: 'Create a new extension from a boilerplate example.',
|
describe: 'Create a new extension from a boilerplate example.',
|
||||||
builder: async (yargs) => {
|
builder: async (yargs) => {
|
||||||
const choices = await getBoilerplateChoices();
|
const choices = await getBoilerplateChoices();
|
||||||
@@ -85,7 +103,7 @@ export const newCommand: CommandModule = {
|
|||||||
handler: async (args) => {
|
handler: async (args) => {
|
||||||
await handleNew({
|
await handleNew({
|
||||||
path: args['path'] as string,
|
path: args['path'] as string,
|
||||||
template: args['template'] as string,
|
template: args['template'] as string | undefined,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user