Skip to content

Commit 97f8268

Browse files
fix(patch): cherry-pick 69f93f8 to release/v0.8.0-preview.1-pr-10629 to patch version v0.8.0-preview.1 and create version 0.8.0-preview.2 (#10642)
Co-authored-by: christine betts <chrstn@uw.edu>
1 parent 17f9d94 commit 97f8268

3 files changed

Lines changed: 43 additions & 19 deletions

File tree

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
{
22
"name": "context-example",
3-
"version": "1.0.0",
4-
"contextFileName": "GEMINI.md"
3+
"version": "1.0.0"
54
}

packages/cli/src/commands/extensions/new.test.ts

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,15 +30,22 @@ describe('extensions new command', () => {
3030
it('should fail if no path is provided', async () => {
3131
const parser = yargs([]).command(newCommand).fail(false).locale('en');
3232
await expect(parser.parseAsync('new')).rejects.toThrow(
33-
'Not enough non-option arguments: got 0, need at least 2',
33+
'Not enough non-option arguments: got 0, need at least 1',
3434
);
3535
});
3636

37-
it('should fail if no template is provided', async () => {
38-
const parser = yargs([]).command(newCommand).fail(false).locale('en');
39-
await expect(parser.parseAsync('new /some/path')).rejects.toThrow(
40-
'Not enough non-option arguments: got 1, need at least 2',
41-
);
37+
it('should create directory when no template is provided', async () => {
38+
mockedFs.access.mockRejectedValue(new Error('ENOENT'));
39+
mockedFs.mkdir.mockResolvedValue(undefined);
40+
41+
const parser = yargs([]).command(newCommand).fail(false);
42+
43+
await parser.parseAsync('new /some/path');
44+
45+
expect(mockedFs.mkdir).toHaveBeenCalledWith('/some/path', {
46+
recursive: true,
47+
});
48+
expect(mockedFs.cp).not.toHaveBeenCalled();
4249
});
4350

4451
it('should create directory and copy files when path does not exist', async () => {

packages/cli/src/commands/extensions/new.ts

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,15 @@
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';
99
import type { CommandModule } from 'yargs';
1010
import { fileURLToPath } from 'node:url';
1111
import { getErrorMessage } from '../../utils/errors.js';
1212

1313
interface NewArgs {
1414
path: string;
15-
template: string;
15+
template?: string;
1616
}
1717

1818
const __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

4751
async 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

6987
export 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

Comments
 (0)