Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 35 additions & 19 deletions packages/schematics/angular/ai-config/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,25 +55,41 @@ interface ContextFileInfo {
}

export default function ({ tool }: ConfigOptions): Rule {
if (!tool) {
return noop();
}
return (tree, context) => {
if (!tool) {
return noop();
}

const rules = tool
.filter((tool) => tool !== Tool.None)
.map((selectedTool) => AI_TOOLS[selectedTool])
.map(({ rulesName, directory, frontmatter }) =>
mergeWith(
apply(url('./files'), [
applyTemplates({
...strings,
rulesName,
frontmatter,
}),
move(directory),
]),
),
);
const rules = tool
.filter((tool) => tool !== Tool.None)
.map((selectedTool) => {
const { rulesName, directory, frontmatter } = AI_TOOLS[selectedTool];
const path = `${directory}/${rulesName}`;

return chain(rules);
if (tree.exists(path)) {
const toolName = strings.classify(selectedTool);
context.logger.warn(
`Skipping configuration file for '${toolName}' at '${path}' because it already exists.\n` +
'This is to prevent overwriting a potentially customized file. ' +
'If you want to regenerate it with Angular recommended defaults, please delete the existing file and re-run the command.\n' +
'You can review the latest recommendations at https://angular.dev/ai/develop-with-ai.',
);

return noop();
}

return mergeWith(
apply(url('./files'), [
applyTemplates({
...strings,
rulesName,
frontmatter,
}),
move(directory),
]),
);
});

return chain(rules);
};
}
22 changes: 22 additions & 0 deletions packages/schematics/angular/ai-config/index_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,28 @@ describe('Ai Config Schematic', () => {
expect(tree.files.length).toBe(filesCount);
});

it('should not overwrite an existing file', async () => {
const customContent = 'custom user content';
workspaceTree.create('.gemini/GEMINI.md', customContent);

const messages: string[] = [];
const loggerSubscription = schematicRunner.logger.subscribe((x) => messages.push(x.message));

try {
const tree = await runConfigSchematic([ConfigTool.Gemini]);

expect(tree.readContent('.gemini/GEMINI.md')).toBe(customContent);
expect(messages).toContain(
`Skipping configuration file for 'Gemini' at '.gemini/GEMINI.md' because it already exists.\n` +
'This is to prevent overwriting a potentially customized file. ' +
'If you want to regenerate it with Angular recommended defaults, please delete the existing file and re-run the command.\n' +
'You can review the latest recommendations at https://angular.dev/ai/develop-with-ai.',
);
} finally {
loggerSubscription.unsubscribe();
}
});

it('should create for tool if None and Gemini are selected', async () => {
const tree = await runConfigSchematic([ConfigTool.Gemini, ConfigTool.None]);
expect(tree.exists('.gemini/GEMINI.md')).toBeTruthy();
Expand Down