Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 63249f9

Browse files
feat(@angular/cli): introduce setup-gemini-cli command with a Gemini CLI extension
Introduces `ng ai setup-gemini-cli` that will automatically install the "Angular Gemini CLI extension", wiring up Angular best practices and rules that improve code generation. In the future we need to look further into: * Documentation * How we version the extension. I.e. could `ng update` help here? * The extension could also wire up the MCP server from the CLI!
1 parent 13b6223 commit 63249f9

File tree

7 files changed

+116
-0
lines changed

7 files changed

+116
-0
lines changed

‎.prettierignore‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,6 @@ dist/
1616
/tests/legacy-cli/e2e/assets/
1717
/tools/test/*.json
1818
pnpm-lock.yaml
19+
20+
# This is a symbolic link.
21+
packages/angular/cli/src/commands/ai/setup-gemini-cli/extension/GEMINI.md
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.dev/license
7+
*/
8+
9+
import { join } from 'node:path';
10+
import { Argv } from 'yargs';
11+
import {
12+
CommandModule,
13+
CommandModuleImplementation,
14+
CommandScope,
15+
Options,
16+
} from '../../command-builder/command-module';
17+
import {
18+
addCommandModuleToYargs,
19+
demandCommandFailureMessage,
20+
} from '../../command-builder/utilities/command';
21+
import SetupGeminiCliModule from './setup-gemini-cli/cli';
22+
23+
export default class AiCommandModule extends CommandModule implements CommandModuleImplementation {
24+
command = 'ai';
25+
describe = 'Commands for artificial intelligence.';
26+
longDescriptionPath = undefined;
27+
override scope = CommandScope.Both;
28+
29+
builder(localYargs: Argv): Argv {
30+
const subcommands = [SetupGeminiCliModule].sort();
31+
32+
for (const module of subcommands) {
33+
addCommandModuleToYargs(module, this.context);
34+
}
35+
36+
return localYargs.demandCommand(1, demandCommandFailureMessage).strict();
37+
}
38+
39+
run(_options: Options<{}>): void {}
40+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.dev/license
7+
*/
8+
9+
import { cp, mkdir } from 'node:fs/promises';
10+
import { homedir } from 'node:os';
11+
import { join } from 'node:path';
12+
import { Argv } from 'yargs';
13+
import {
14+
CommandModule,
15+
CommandModuleImplementation,
16+
CommandScope,
17+
Options,
18+
} from '../../../command-builder/command-module';
19+
20+
export default class SetupGeminiCliModule
21+
extends CommandModule
22+
implements CommandModuleImplementation
23+
{
24+
command = 'setup-gemini-cli';
25+
describe = 'Sets up Gemini CLI with the official Angular extension.';
26+
longDescriptionPath?: string | undefined;
27+
28+
override scope = CommandScope.Both;
29+
30+
builder(localYargs: Argv): Argv {
31+
return localYargs.strict();
32+
}
33+
34+
async run(_options: Options<{}>): Promise<void> {
35+
const extensionDir = join(__dirname, './extension');
36+
const extensionUserDir = join(homedir(), '.gemini', 'extensions', 'angular');
37+
38+
await mkdir(extensionUserDir, { recursive: true });
39+
await cp(extensionDir, extensionUserDir, { recursive: true, dereference: true });
40+
41+
this.context.logger.info(`✅ Installed the Angular Gemini CLI extension.`);
42+
}
43+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../../../mcp/instructions/best-practices.md
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"name": "Angular",
3+
"version": "0.0.0-PLACEHOLDER",
4+
"mcpServers": {},
5+
"contextFileName": "./GEMINI.md"
6+
}

‎packages/angular/cli/src/commands/command-config.ts‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { CommandModuleConstructor } from '../command-builder/utilities/command';
1010

1111
export type CommandNames =
1212
| 'add'
13+
| 'ai'
1314
| 'analytics'
1415
| 'build'
1516
| 'cache'
@@ -41,6 +42,9 @@ export const RootCommands: Record<
4142
'add': {
4243
factory: () => import('./add/cli'),
4344
},
45+
'ai': {
46+
factory: () => import('./ai/cli'),
47+
},
4448
'analytics': {
4549
factory: () => import('./analytics/cli'),
4650
},
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { join } from 'node:path';
2+
import { expectFileNotToExist, expectFileToExist, expectFileToMatch } from '../../../utils/fs';
3+
import { ng } from '../../../utils/process';
4+
import assert from 'node:assert';
5+
6+
export default async function () {
7+
assert(process.env.HOME, 'Expected HOME directory to be set.');
8+
9+
const extensionDir = join(process.env.HOME, '.gemini', 'extensions', 'angular');
10+
const geminiBestPracticesFile = join(extensionDir, 'GEMINI.md');
11+
12+
await expectFileNotToExist(extensionDir);
13+
await expectFileNotToExist(geminiBestPracticesFile);
14+
await ng('ai', 'setup-gemini-cli');
15+
16+
await expectFileToExist(extensionDir);
17+
await expectFileToExist(geminiBestPracticesFile);
18+
await expectFileToMatch(geminiBestPracticesFile, 'Angular Best Practices');
19+
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /