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
6 changes: 6 additions & 0 deletions apps/docs-analog/src/content/integrations/nx/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,3 +83,9 @@ Next, use the application generator to scaffold a new application:
```shell
npx nx g @analogjs/platform:application analog-app
```

## Agent Context

Both the preset and the application generator scaffold `AGENTS.md` and `CLAUDE.md` files that point AI coding assistants at the framework conventions shipped in `node_modules/@analogjs/platform/AGENTS.md`.

The preset writes them at the workspace root, since the workspace is created for Analog. The application generator writes them in the application folder, such as `apps/analog-app`, so the guidance stays scoped to the Analog application in a workspace that may contain other projects. Existing `AGENTS.md` and `CLAUDE.md` files are never overwritten.
27 changes: 27 additions & 0 deletions packages/nx-plugin/src/generators/app/generator.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -197,5 +197,32 @@ describe('nx-plugin generator', () => {
verifyHomePageExists(tree, analogAppName);
verifyTagsArePopulated(config, ['tag1', 'tag2', 'type:app']);
});

it('generates agent context in the app wired to @analogjs/platform', async () => {
const analogAppName = 'agents-app';
const { tree } = await setup({ analogAppName });

expect(tree.read(`apps/${analogAppName}/AGENTS.md`).toString()).toContain(
'node_modules/@analogjs/platform/AGENTS.md',
);
expect(tree.read(`apps/${analogAppName}/CLAUDE.md`).toString()).toContain(
'@AGENTS.md',
);
});

it('does not overwrite existing agent context in the app', async () => {
const analogAppName = 'existing-agents-app';
const tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });

addDependenciesToPackageJson(tree, {}, { nx: '21.0.0' });
tree.write(`apps/${analogAppName}/AGENTS.md`, '# Custom guidance');

await generator(tree, { analogAppName });

expect(tree.read(`apps/${analogAppName}/AGENTS.md`).toString()).toContain(
'# Custom guidance',
);
expect(tree.exists(`apps/${analogAppName}/CLAUDE.md`)).toBe(true);
Comment on lines +213 to +225

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Cover preservation for both agent files.

The test seeds only AGENTS.md. It does not prove that an existing CLAUDE.md survives. Seed both files and assert that both custom contents remain after generation.

Proposed test update
       tree.write(`apps/${analogAppName}/AGENTS.md`, '# Custom guidance');
+      tree.write(
+        `apps/${analogAppName}/CLAUDE.md`,
+        '# Custom Claude guidance',
+      );

       await generator(tree, { analogAppName });

       expect(tree.read(`apps/${analogAppName}/AGENTS.md`).toString()).toContain(
         '# Custom guidance',
       );
+      expect(tree.read(`apps/${analogAppName}/CLAUDE.md`).toString()).toContain(
+        '# Custom Claude guidance',
+      );
       expect(tree.exists(`apps/${analogAppName}/CLAUDE.md`)).toBe(true);

As per coding guidelines, tests must validate behavior for new functionality.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
it('does not overwrite existing agent context in the app', async () => {
const analogAppName = 'existing-agents-app';
const tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
addDependenciesToPackageJson(tree, {}, { nx: '21.0.0' });
tree.write(`apps/${analogAppName}/AGENTS.md`, '# Custom guidance');
await generator(tree, { analogAppName });
expect(tree.read(`apps/${analogAppName}/AGENTS.md`).toString()).toContain(
'# Custom guidance',
);
expect(tree.exists(`apps/${analogAppName}/CLAUDE.md`)).toBe(true);
it('does not overwrite existing agent context in the app', async () => {
const analogAppName = 'existing-agents-app';
const tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
addDependenciesToPackageJson(tree, {}, { nx: '21.0.0' });
tree.write(`apps/${analogAppName}/AGENTS.md`, '# Custom guidance');
tree.write(
`apps/${analogAppName}/CLAUDE.md`,
'# Custom Claude guidance',
);
await generator(tree, { analogAppName });
expect(tree.read(`apps/${analogAppName}/AGENTS.md`).toString()).toContain(
'# Custom guidance',
);
expect(tree.read(`apps/${analogAppName}/CLAUDE.md`).toString()).toContain(
'# Custom Claude guidance',
);
expect(tree.exists(`apps/${analogAppName}/CLAUDE.md`)).toBe(true);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/nx-plugin/src/generators/app/generator.spec.ts` around lines 213 -
225, Update the “does not overwrite existing agent context in the app” test to
seed both AGENTS.md and CLAUDE.md with custom content before calling generator.
Assert that each file still exists and contains its respective custom content
after generation, preserving the existing test setup and generator invocation.

Source: Coding guidelines

});
});
});
5 changes: 5 additions & 0 deletions packages/nx-plugin/src/generators/app/generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { belowMinimumSupportedNxVersion } from './versions/minimum-supported-ver
import { addAngularApp } from './lib/add-angular-app';
import setupAnalogGenerator from '../init/generator';
import { addFiles } from './lib/add-files';
import { addAgentContext } from './lib/add-agent-context';
import { addTailwindConfig } from './lib/add-tailwind-config';
import { cleanupFiles } from './lib/cleanup-files';
import { addAnalogProjectConfig } from './lib/add-analog-project-config';
Expand Down Expand Up @@ -140,6 +141,10 @@ export async function appGenerator(

addHomePage(tree, normalizedOptions, majorAngularVersion);

if (!normalizedOptions.skipAgentContext) {
addAgentContext(tree, normalizedOptions.projectRoot);
}

cleanupFiles(tree, normalizedOptions);

if (!normalizedOptions.skipFormat) {
Expand Down
22 changes: 22 additions & 0 deletions packages/nx-plugin/src/generators/app/lib/add-agent-context.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { Tree } from '@nx/devkit';
import { readFileSync } from 'node:fs';
import { join } from 'node:path';

const AGENT_CONTEXT_FILES = ['AGENTS.md', 'CLAUDE.md'];

// Seeds agent context in the app so AI coding assistants pick up Analog
// conventions (see node_modules/@analogjs/platform/AGENTS.md).
export function addAgentContext(tree: Tree, projectRoot: string) {
for (const fileName of AGENT_CONTEXT_FILES) {
const filePath = `${projectRoot}/${fileName}`;

if (tree.exists(filePath)) {
continue;
}

tree.write(
filePath,
readFileSync(join(__dirname, '..', 'files', 'agents', fileName), 'utf-8'),
);
}
}
2 changes: 2 additions & 0 deletions packages/nx-plugin/src/generators/app/schema.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,6 @@ export interface AnalogNxApplicationGeneratorOptions {
tags?: string;
addTailwind?: boolean;
skipFormat?: boolean;
// Set by the preset, which seeds agent context at the workspace root instead.
skipAgentContext?: boolean;
}
2 changes: 2 additions & 0 deletions packages/nx-plugin/src/generators/preset/generator.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ describe('preset generator', () => {
'node_modules/@analogjs/platform/AGENTS.md',
);
expect(tree.read('/CLAUDE.md').toString()).toContain('@AGENTS.md');
expect(tree.exists('/my-app/AGENTS.md')).toBe(false);
expect(tree.exists('/my-app/CLAUDE.md')).toBe(false);
});

it('should use vitest 3 for Nx < 22.3.0', async () => {
Expand Down
9 changes: 7 additions & 2 deletions packages/nx-plugin/src/generators/preset/generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,17 @@ export default async function (tree: Tree, options: PresetGeneratorSchema) {
ensurePackage('rxjs', 'latest');

const appTask = await import('../app/generator').then(({ appGenerator }) =>
appGenerator(tree, options),
appGenerator(tree, { ...options, skipAgentContext: true }),
);

// Seed agent context at the workspace root so AI coding assistants pick up
// Analog conventions (see node_modules/@analogjs/platform/AGENTS.md).
generateFiles(tree, join(__dirname, 'files'), '.', options);
generateFiles(
tree,
join(__dirname, '..', 'app', 'files', 'agents'),
'.',
options,
);
Comment on lines +18 to +23

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== workspace files =="
git ls-files | rg '(^packages/nx-plugin/src/generators/preset/generator\.ts$|^apps/docs-analog/src/content/integrations/nx/index\.md$|^workspace\.json$|^nx\.json$|^package\.json$|jest|test|migrations|schematics|generators|agents|CLAUDE|AGENTS)' | head -200

echo
echo "== target preset generator =="
if [ -f packages/nx-plugin/src/generators/preset/generator.ts ]; then
  cat -n packages/nx-plugin/src/generators/preset/generator.ts
fi

echo
echo "== generator files snippets =="
fd -t f 'generator\.ts$' packages/nx-plugin/src/generators | while read -r f; do
  echo "--- $f"
  rg -n "generateFiles|OverwriteStrategy|addAgentContext|CLAUDE|AGENTS|agents" "$f" || true
done

echo
echo "== app/agenerator files snippets =="
fd -t f 'generator\.ts$' packages/nx-plugin/src/generators | while read -r f; do
  echo "--- $f"
  rg -n "generateFiles\(|OverwriteStrategy|addAgentContext|AGENTS|CLAUDE|agents" "$f" || true
done

Repository: analogjs/analog

Length of output: 14279


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== package deps nx version =="
node - <<'JS'
const fs = require('fs');
const p = JSON.parse(fs.readFileSync('package.json','utf8'));
if (p.devDependencies?.nx) console.log('package.json devDependencies.nx:', p.devDependencies.nx);
if (p.dependencies?.nx) console.log('package.json dependencies.nx:', p.dependencies.nx);
JS

echo
echo "== preset generator =="
cat -n packages/nx-plugin/src/generators/preset/generator.ts

echo
echo "== app generator relevant sections =="
cat -n packages/nx-plugin/src/generators/app/generator.ts | sed -n '1,220p'

echo
echo "== references to generateFiles and KeepExisting =="rg
rg -n "generateFiles\(|OverwriteStrategy|KeepExisting|addAgentContext|AGENTS\.md|CLAUDE\.md|agents" packages/nx-plugin packages -g '*.ts' -g '*.json' | head -200

echo
echo "== tests relevant references =="
rg -n "preset|generateFiles|KeepExisting|AGENTS|CLAUDE|agent" packages/nx-plugin -g '*.spec.ts' -g '*.ts' | head -200

Repository: analogjs/analog

Length of output: 21086


🌐 Web query:

Nx generateFiles default overwrite strategy OverwriteStrategy KeepExisting

💡 Result:

In Nx, the generateFiles function supports an optional configuration to control how existing files are handled using the OverwriteStrategy [1][2]. To use the KeepExisting strategy, you pass an options object as the final argument to generateFiles [1][3]: generateFiles(tree, srcFolder, target, substitutions, { overwriteStrategy: OverwriteStrategy.KeepExisting }); Available strategies in the OverwriteStrategy enumeration include [4][5]: OverwriteStrategy.Overwrite (default): Automatically overwrites existing files [1][3]. OverwriteStrategy.KeepExisting: Only creates files if the target does not already exist; existing files are preserved [1][3]. OverwriteStrategy.ThrowIfExisting: Throws an exception if a target file already exists, which is useful when a pristine environment is required [1][3].

Citations:


Preserve existing root agent context files in preset-generated workspaces.

Preset agent context generation uses generateFiles with Nx’s default overwrite strategy, so root AGENTS.md or CLAUDE.md files can be replaced. Implement the same existing-file safeguard documented for app-level agent context at the workspace root, and add a regression test.

  • packages/nx-plugin/src/generators/preset/generator.ts#L18-L23: Use the existing-file-safe agent context path/strategy.
  • apps/docs-analog/src/content/integrations/nx/index.md#L89-91: Update this claim to describe when existing root files are preserved rather than saying they are never overwritten.
📍 Affects 2 files
  • packages/nx-plugin/src/generators/preset/generator.ts#L18-L23 (this comment)
  • apps/docs-analog/src/content/integrations/nx/index.md#L89-L91
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/nx-plugin/src/generators/preset/generator.ts` around lines 18 - 23,
Update the preset generator’s generateFiles call in
packages/nx-plugin/src/generators/preset/generator.ts:18-23 to use the
existing-file-safe agent context path/strategy already used by the app-level
generator, and add a regression test confirming root AGENTS.md and CLAUDE.md
files are preserved. Update
apps/docs-analog/src/content/integrations/nx/index.md:89-91 to state the
conditions under which existing root context files are preserved instead of
claiming they are never overwritten.

Source: Path instructions


return appTask;
}
Loading