-
Notifications
You must be signed in to change notification settings - Fork 2.6k
feat(core): generate AI files in create-nx-workspace #32442
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
The latest updates on your projects. Learn more about Vercel for GitHub.
|
8715f7a
to
65e4cb2
Compare
65e4cb2
to
7f328ca
Compare
View your CI Pipeline Execution ↗ for commit 7e8a0b2
☁️ Nx Cloud last updated this comment at |
c0d176d
to
34cc4f5
Compare
a833914
to
762aea2
Compare
🐳 We have a release for that!This PR has a release associated with it. You can try it out using this command: npx [email protected] my-workspace Or just copy this version and use it in your own command: 0.0.0-pr-32442-762aea2
To request a new release for this pull request, mention someone from the Nx team or the |
try { | ||
const getLatestGeneratorResult = await getLatestGeneratorUsingInstall( | ||
normalizedOptions | ||
); | ||
const { module: latestGeneratorModule, cleanup } = getLatestGeneratorResult; | ||
const setupAiAgentsGeneratorResult = | ||
await latestGeneratorModule.setupAiAgentsGeneratorImpl(tree, options); | ||
await cleanup(); | ||
return setupAiAgentsGeneratorResult; | ||
} catch (error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's a potential null reference issue in this code. The getLatestGeneratorUsingInstall
function can return undefined
, but the code immediately destructures the result without checking. If getLatestGeneratorResult
is undefined
, the line const { module: latestGeneratorModule, cleanup } = getLatestGeneratorResult;
will throw a runtime error.
Consider adding a null check before destructuring:
const getLatestGeneratorResult = await getLatestGeneratorUsingInstall(normalizedOptions);
if (getLatestGeneratorResult) {
const { module: latestGeneratorModule, cleanup } = getLatestGeneratorResult;
const setupAiAgentsGeneratorResult =
await latestGeneratorModule.setupAiAgentsGeneratorImpl(tree, options);
await cleanup();
return setupAiAgentsGeneratorResult;
}
try { | |
const getLatestGeneratorResult = await getLatestGeneratorUsingInstall( | |
normalizedOptions | |
); | |
const { module: latestGeneratorModule, cleanup } = getLatestGeneratorResult; | |
const setupAiAgentsGeneratorResult = | |
await latestGeneratorModule.setupAiAgentsGeneratorImpl(tree, options); | |
await cleanup(); | |
return setupAiAgentsGeneratorResult; | |
} catch (error) { | |
try { | |
const getLatestGeneratorResult = await getLatestGeneratorUsingInstall( | |
normalizedOptions | |
); | |
if (getLatestGeneratorResult) { | |
const { module: latestGeneratorModule, cleanup } = getLatestGeneratorResult; | |
const setupAiAgentsGeneratorResult = | |
await latestGeneratorModule.setupAiAgentsGeneratorImpl(tree, options); | |
await cleanup(); | |
return setupAiAgentsGeneratorResult; | |
} | |
} catch (error) { |
Spotted by Diamond
Is this helpful? React 👍 or 👎 to let us know.
6b5cf77
to
e8b491e
Compare
5710028
to
d5316c8
Compare
packages/workspace/src/generators/set-up-ai-agents/set-up-ai-agents.ts
Outdated
Show resolved
Hide resolved
@@ -6,7 +6,7 @@ sidebar: | |||
label: AI Integration | |||
--- | |||
|
|||
Nx provides deep integration with AI coding assistants through the **Nx Model Context Protocol (MCP) server**, giving your AI assistant comprehensive understanding of your monorepo structure, running processes, and development workflows. | |||
Nx provides deep integration with AI coding assistants through the **Nx Model Context Protocol (MCP) server**, giving your AI assistant comprehensive understanding of your monorepo structure, running processes, and development workflows. When you create a new Nx workspace, it includes AI agent configuration files (`CLAUDE.md` and `GEMINI.md`) that provide guidelines for working with Nx and the Nx MCP server. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The documentation mentions only CLAUDE.md
and GEMINI.md
files, but the implementation also creates an AGENTS.md
file. This creates an inconsistency between what's documented and what's actually generated. Consider either updating the documentation to mention all generated files or adjusting the implementation to match what's documented to ensure users have accurate information about the workspace setup.
Nx provides deep integration with AI coding assistants through the **Nx Model Context Protocol (MCP) server**, giving your AI assistant comprehensive understanding of your monorepo structure, running processes, and development workflows. When you create a new Nx workspace, it includes AI agent configuration files (`CLAUDE.md` and `GEMINI.md`) that provide guidelines for working with Nx and the Nx MCP server. | |
Nx provides deep integration with AI coding assistants through the **Nx Model Context Protocol (MCP) server**, giving your AI assistant comprehensive understanding of your monorepo structure, running processes, and development workflows. When you create a new Nx workspace, it includes AI agent configuration files (`AGENTS.md`, `CLAUDE.md`, and `GEMINI.md`) that provide guidelines for working with Nx and the Nx MCP server. |
Spotted by Diamond
Is this helpful? React 👍 or 👎 to let us know.
54af601
to
ea3ba20
Compare
const geminPath = join(options.directory, '.gemini', 'settings.json'); | ||
addMcpConfigToJson(tree, geminPath); | ||
updateJson(tree, geminPath, (json) => ({ | ||
...json, | ||
contextFileName: 'AGENTS.md', | ||
})); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There appears to be a naming inconsistency in the AI agent file configuration. The code sets contextFileName: 'AGENTS.md'
for Gemini configuration, but the documentation and tests reference both CLAUDE.md
and GEMINI.md
files.
The implementation generates files with different names:
- In the file generation section, it creates
CLAUDE.md
andAGENTS.md
- In the tests, it expects
CLAUDE.md
andGEMINI.md
- The documentation mentions
CLAUDE.md
andAGENTS.md
This inconsistency could lead to confusion for users and potential issues with AI assistant integration. Consider standardizing the naming convention across the codebase to ensure that the generated files, configuration settings, and documentation all align with the same file naming pattern.
const geminPath = join(options.directory, '.gemini', 'settings.json'); | |
addMcpConfigToJson(tree, geminPath); | |
updateJson(tree, geminPath, (json) => ({ | |
...json, | |
contextFileName: 'AGENTS.md', | |
})); | |
const geminPath = join(options.directory, '.gemini', 'settings.json'); | |
addMcpConfigToJson(tree, geminPath); | |
updateJson(tree, geminPath, (json) => ({ | |
...json, | |
contextFileName: 'GEMINI.md', | |
})); | |
Spotted by Diamond
Is this helpful? React 👍 or 👎 to let us know.
packages/workspace/src/generators/set-up-ai-agents/set-up-ai-agents.ts
Outdated
Show resolved
Hide resolved
function addMcpConfigToJson(tree: Tree, path: string) { | ||
let mcpConfig: any = {}; | ||
|
||
if (tree.exists(path)) { | ||
const mcpContent = tree.read(path).toString(); | ||
mcpConfig = JSON.parse(mcpContent); | ||
} | ||
|
||
if (mcpConfig.mcpServers) { | ||
mcpConfig.mcpServers['nx-mcp'] = { | ||
type: 'stdio', | ||
command: 'npx', | ||
args: ['nx', 'mcp'], | ||
}; | ||
} else { | ||
mcpConfig.mcpServers = { | ||
'nx-mcp': { | ||
type: 'stdio', | ||
command: 'npx', | ||
args: ['nx', 'mcp'], | ||
}, | ||
}; | ||
} | ||
tree.write(path, JSON.stringify(mcpConfig, null, 2)); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The addMcpConfigToJson
function lacks error handling for JSON parsing. If an existing configuration file contains invalid JSON, the JSON.parse(mcpContent)
call will throw an exception, potentially leaving the tree in an inconsistent state. Consider adding a try/catch block to gracefully handle malformed JSON:
try {
mcpConfig = JSON.parse(mcpContent);
} catch (error) {
// Either log the error and continue with empty config
// or throw a more descriptive error
console.warn(`Invalid JSON in ${path}, creating new configuration`);
// mcpConfig remains an empty object
}
This would prevent configuration corruption when encountering invalid JSON files.
function addMcpConfigToJson(tree: Tree, path: string) { | |
let mcpConfig: any = {}; | |
if (tree.exists(path)) { | |
const mcpContent = tree.read(path).toString(); | |
mcpConfig = JSON.parse(mcpContent); | |
} | |
if (mcpConfig.mcpServers) { | |
mcpConfig.mcpServers['nx-mcp'] = { | |
type: 'stdio', | |
command: 'npx', | |
args: ['nx', 'mcp'], | |
}; | |
} else { | |
mcpConfig.mcpServers = { | |
'nx-mcp': { | |
type: 'stdio', | |
command: 'npx', | |
args: ['nx', 'mcp'], | |
}, | |
}; | |
} | |
tree.write(path, JSON.stringify(mcpConfig, null, 2)); | |
} | |
function addMcpConfigToJson(tree: Tree, path: string) { | |
let mcpConfig: any = {}; | |
if (tree.exists(path)) { | |
const mcpContent = tree.read(path).toString(); | |
try { | |
mcpConfig = JSON.parse(mcpContent); | |
} catch (error) { | |
console.warn(`Invalid JSON in ${path}, creating new configuration`); | |
// mcpConfig remains an empty object | |
} | |
} | |
if (mcpConfig.mcpServers) { | |
mcpConfig.mcpServers['nx-mcp'] = { | |
type: 'stdio', | |
command: 'npx', | |
args: ['nx', 'mcp'], | |
}; | |
} else { | |
mcpConfig.mcpServers = { | |
'nx-mcp': { | |
type: 'stdio', | |
command: 'npx', | |
args: ['nx', 'mcp'], | |
}, | |
}; | |
} | |
tree.write(path, JSON.stringify(mcpConfig, null, 2)); | |
} | |
Spotted by Diamond
Is this helpful? React 👍 or 👎 to let us know.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nx Cloud is proposing a fix for your failed CI:
We've identified and fixed the issue where the setupAiAgentsGenerator was not generating the GEMINI.md file as expected by the tests. The generator now includes logic to generate the GEMINI.md file when it doesn't exist, matching the pattern used for CLAUDE.md and AGENTS.md files.
We verified this fix by re-running workspace:test
.
diff --git a/packages/workspace/src/generators/set-up-ai-agents/set-up-ai-agents.ts b/packages/workspace/src/generators/set-up-ai-agents/set-up-ai-agents.ts
index 0bf106c..9fa95e5 100644
--- a/packages/workspace/src/generators/set-up-ai-agents/set-up-ai-agents.ts
+++ b/packages/workspace/src/generators/set-up-ai-agents/set-up-ai-agents.ts
@@ -97,6 +97,12 @@ export async function setupAiAgentsGeneratorImpl(
agent: 'CLAUDE',
});
}
+ if (!tree.exists(join(options.directory, 'GEMINI.md'))) {
+ generateFiles(tree, join(__dirname, './files'), options.directory, {
+ writeNxCloudRules: options.writeNxCloudRules,
+ agent: 'GEMINI',
+ });
+ }
if (!tree.exists(join(options.directory, 'AGENTS.md'))) {
generateFiles(tree, join(__dirname, './files'), options.directory, {
writeNxCloudRules: options.writeNxCloudRules,
⚙️ An Nx Cloud workspace admin can disable these reviews in workspace settings.
} catch { | ||
await cleanup(); | ||
return undefined; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Potential resource leak: The error handling in this catch block returns undefined
after calling cleanup()
, but the caller at line 44 will attempt to destructure and call cleanup()
again on this undefined value, resulting in a runtime error. Consider either:
- Rethrowing the error after cleanup:
try {
// ...
} catch (error) {
await cleanup();
throw error;
}
- Or returning an object with a no-op cleanup function:
try {
// ...
} catch (error) {
await cleanup();
return { module: null, cleanup: async () => {} };
}
This ensures the caller can safely destructure and call the cleanup function regardless of whether the import succeeds.
} catch { | |
await cleanup(); | |
return undefined; | |
} catch (error) { | |
await cleanup() | |
return { module: null, cleanup: async () => {} } |
Spotted by Diamond
Is this helpful? React 👍 or 👎 to let us know.
@@ -16,7 +16,8 @@ export async function ensurePackageHasProvenance( | |||
): Promise<void> { | |||
// this is used for locally released versions without provenance | |||
// do not set this for other reasons or you might be exposed to security risks | |||
if (process.env.NX_SKIP_PROVENANCE_CHECK) { | |||
// also skip this check during e2e tests because those rely on local packages without provenance | |||
if (process.env.NX_SKIP_PROVENANCE_CHECK || process.env.NX_E2E_RUN_E2E) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just set NX_SKIP_PROVENANCE_CHECK in e2e
@@ -0,0 +1,154 @@ | |||
import { formatFiles, generateFiles, Tree } from '@nx/devkit'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Forego generateFiles and we can move this into nx
async function getLatestGeneratorUsingInstall( | ||
options: NormalizedSetupAiAgentsGeneratorSchema | ||
): Promise<{ module: any; cleanup: () => Promise<void> } | undefined> { | ||
const { dir, cleanup } = createTempNpmDirectory(true); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use ensurePackage
for this.
}, | ||
}; | ||
} | ||
tree.write(path, JSON.stringify(mcpConfig, null, 2)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
use writeJson
or updateJson
which will handle trailing commas and comments in the JSON files.
This pull request introduces a new generator to automatically set up AI agent configuration files and Nx MCP server integration when creating a new Nx workspace. It ensures that guidelines and configuration for common AI assistants (Claude and Gemini) are included by default, improving developer experience and AI integration. The changes also update documentation to reflect these enhancements.
AI Agent Setup and Integration
set-up-ai-agents
, topackages/workspace/generators.json
that generates configuration files (CLAUDE.md
,GEMINI.md
,.mcp.json
, and.gemini/settings.json
) with guidelines and Nx MCP server settings for AI assistants. [1] [2] [3] [4] [5]setupAiAgentsGenerator
ingenerate-workspace-files.ts
, ensuring these files are created for every new workspace. [1] [2]Documentation Updates
astro-docs
and shared docs to mention the inclusion of AI agent configuration files (CLAUDE.md
,GEMINI.md
) during workspace creation. [1] [2]Internal Improvements
These changes collectively streamline AI assistant onboarding for Nx users and ensure that new workspaces are ready for advanced AI-powered development workflows.