Compare commits

...

261 Commits

Author SHA1 Message Date
Jason Jean b05d5fee28 chore(core): retrigger ci 2026-02-27 08:46:40 -05:00
nx-cloud[bot] 14c2d5ba3f test(core): update tests for new PluginCache API
Co-authored-by: FrozenPandaz <FrozenPandaz@users.noreply.github.com>
2026-02-27 13:28:28 +00:00
Jason Jean c43ee13eed feat(core): add safe plugin cache write utilities with LRU eviction
Add PluginCache<T> class with explicit get/set API that tracks access
order for LRU eviction. Dedup and capping happen at write time, not
per-access. All plugin cache writes are now wrapped in try/catch so
failures never crash the project graph calculation.

Migrated consumers: cypress, playwright, dotnet, gradle (v1+v2),
maven, package-json, js/lockfile, nx-deps-cache.
2026-02-27 00:22:49 -05:00
Leosvel Pérez Espinosa af07e75d58 feat(core): use jemalloc with tuned decay timers for native module (#34444)
## Current Behavior

The Nx native module (Rust cdylib loaded by Node.js) uses the system
allocator. The daemon process retains a large RSS footprint after the
initial project graph build, even though most of that memory is no
longer in use. On macOS and Linux, the system allocator doesn't
aggressively return freed pages to the OS.

## Expected Behavior

The daemon's steady-state RSS drops significantly after graph build by
using jemalloc with tuned page purge timers. Peak RSS and wall time are
unaffected.

## Changes

Adds [tikv-jemallocator](https://github.com/tikv/jemallocator) as the
global allocator on Linux and macOS, with two compile-time settings:

- **`dirty_decay_ms:1000`** — returns freed pages to the OS after 1s
instead of the default 10s. Tuned to Nx's phase-separated workload
(graph build → idle → task execution), where transitions happen every
~30-60s. Benchmarked against 5s and 10s — both too slow to purge between
phases.
- **`muzzy_decay_ms:0`** — skips the lazy purge phase (`MADV_FREE`) and
goes straight to `MADV_DONTNEED`. Required on macOS and Linux ≥ 4.5
where `MADV_FREE` doesn't actually reduce RSS.

Windows and WASI continue using the system allocator. Windows is
excluded because `tikv-jemalloc-sys` fails to build with MSVC (spaces in
`cl.exe` path break the autoconf configure script). Tracked upstream in
[tikv/jemallocator#99](https://github.com/tikv/jemallocator/pull/99).

### Other Settings Considered

Tested narenas reduction, tcache_max, extent fit tuning, background
threads, and decay timer values. Only the decay timer configuration
improved steady-state RSS without wall time regression.
2026-02-26 23:47:47 -05:00
Jesse Zomer ac2ef1aaef fix(linter): allow for wildcards paths in enforce-module-boundaries rule (#34066)
closed #32190

## Current Behavior

eslint crashes when tsconfig.base.json path includes a * and you have an
import going to that project
## Expected Behavior
The plugin shouldn't crash and it should auto fix to a working import

## Related Issue(s)
<!-- Please link the issue being fixed so it gets closed when this is
merged. -->
https://github.com/nrwl/nx/issues/32190

Fixes #32190
2026-02-26 16:21:10 -05:00
Jason Jean 191054d876 chore(repo): update nx to 22.6.0-beta.5 (#34618)
Updating Nx from 22.6.0-beta.3 to 22.6.0-beta.5

---------

Co-authored-by: nx-cloud[bot] <71083854+nx-cloud[bot]@users.noreply.github.com>
2026-02-26 15:40:35 -05:00
Eric Baer b8b6ed8b85 fix(testing): use surgical text replacement in Jest matcher alias migration (#34350)
## Current Behavior

The `replace-removed-matcher-aliases` migration uses `tsquery.replace()`
which reprints the entire AST through TypeScript's Printer. This causes
two problems:

1. **Syntax corruption**: Valid TypeScript files are mangled:
   - Destructuring patterns: `{ result }` becomes `{result}:`
   - Arrow functions: missing opening braces
   - Nested callbacks: collapsed/merged code blocks

2. **Unnecessary file changes**: Every test file is written back to disk
even when no matchers are replaced. This triggers `formatFiles()` to
reformat unchanged files, creating large whitespace-only diffs. In large
codebases, this can result in hundreds or thousands of files being
modified unnecessarily, making the migration PR difficult to review.

**Why I care a Lot**

I was running this on a multi-million-LOC monorepo and ran into two
issues:

* I got ~10k modified files with whitespace-only changes from the
removal of newlines. These changes couldn't be fixed with Prettier
because it didn't care about the number of newlines, so the diff was
unmergeable.
* I got ~8 files with malformed Typescript, causing commit hooks, CI,
etc. to fail without manual intervention.

## Expected Behavior

The migration should:
1. Only replace the deprecated matcher names (e.g., `toBeCalled` →
`toHaveBeenCalled`)
2. Preserve all surrounding code exactly as written
5. Only touch files that actually contain deprecated matchers

## Solution

Replace AST-reprinting with surgical text replacement:
- Use `tsquery.query()` to find matching AST nodes
- Collect text positions (start/end) for each node to replace
- Apply replacements in reverse order using string slicing
- Only write files that actually changed

This pattern is already used successfully in other Nx migrations (e.g.,
`rename-cy-exec-code-property.ts` in the Cypress package).

**Additional improvements:**
- Single AST parse with regex selector vs. 11 separate passes
- Quick string check skips parsing files without deprecated matchers
- New regression test covers complex patterns that triggered corruption

## Related Issue(s)

Fixes #32062

---------

Co-authored-by: Jack Hsu <jack.hsu@gmail.com>
2026-02-26 15:20:15 -05:00
Jason Jean bdc61b5ad5 chore(repo): improve e2e test timeout handling and bump cache bust (#34383)
## Current Behavior

E2E tests may timeout without clear error messages, making it difficult
to diagnose test failures.

## Expected Behavior

E2E test utilities should provide better timeout handling and logging to
help diagnose test failures.

## Changes

- Add timeout handling to e2e test utilities (`runCLI` and
`runLernaCLI`)
- Add command logging to track execution time
- Improve timeout error messages with process output
- Bump cache bust value

## Related Issue(s)

CI stability and debugging improvements
2026-02-26 13:13:34 -05:00
Jason Weinzierl 31dad4109b fix(linter): support eslint v10 (#34534)
<!-- Please make sure you have read the submission guidelines before
posting an PR -->
<!--
https://github.com/nrwl/nx/blob/master/CONTRIBUTING.md#-submitting-a-pr
-->

<!-- Please make sure that your commit message follows our format -->
<!-- Example: `fix(nx): must begin with lowercase` -->

<!-- If this is a particularly complex change or feature addition, you
can request a dedicated Nx release for this pull request branch. Mention
someone from the Nx team or the `@nrwl/nx-pipelines-reviewers` and they
will confirm if the PR warrants its own release for testing purposes,
and generate it for you if appropriate. -->

## Current Behavior

`@nx/eslint` relies on ESLint internals that changed in ESLint v10
(`use-at-your-own-risk`), which causes failures.

It looks like https://github.com/nrwl/nx/pull/24632 originally attempted
to use `loadESLint()` which would've been forward compatible with v10,
but it was later removed in https://github.com/nrwl/nx/pull/27404 in
favor of the `use-at-your-own-risk` import.

## Expected Behavior

`@nx/eslint` supports ESLint v10.

## Related Issue(s)
<!-- Please link the issue being fixed so it gets closed when this is
merged. -->

Fixes #34415
2026-02-26 12:35:59 -05:00
omasakun c3643126ef fix(core): make watch command work with all and initialRun specified (#32282)
<!-- Please make sure you have read the submission guidelines before
posting an PR -->
<!--
https://github.com/nrwl/nx/blob/master/CONTRIBUTING.md#-submitting-a-pr
-->

<!-- Please make sure that your commit message follows our format -->
<!-- Example: `fix(nx): must begin with lowercase` -->

<!-- If this is a particularly complex change or feature addition, you
can request a dedicated Nx release for this pull request branch. Mention
someone from the Nx team or the `@nrwl/nx-pipelines-reviewers` and they
will confirm if the PR warrants its own release for testing purposes,
and generate it for you if appropriate. -->

## Current Behavior
<!-- This is the behavior we have today -->

If you specify both the `all` and `initialRun` options when running `nx
watch`, `initialRun` have no effect.

## Expected Behavior
<!-- This is the behavior we should expect with the changes in this PR
-->

The command should be called once at the beginning even if there are no
file changes.

## Related Issue(s)
<!-- Please link the issue being fixed so it gets closed when this is
merged. -->

Fixes #32281
2026-02-26 11:45:51 -05:00
Anurag Agarwal dcfc2134d4 fix(maven): fix set the pom file without changing base directory (#34182)
## Current Behavior
nx-maven-plugin 0.0.12 is also changing the base directory along with
the pom file when the plugins like flatten-maven-plugin /
maven-shade-plugin produces pomFile in different directory other than
the base directory

## Expected Behavior
plugin should only update the pom file and not the base directory
location

## Related Issue(s)

Fixes #34181

https://github.com/mojohaus/flatten-maven-plugin/issues/50

Co-authored-by: anurag.ag <anuragagarwal561994@users.noreply.github.com>
2026-02-26 11:20:42 -05:00
Jack Hsu 77100fac5d docs(misc): add Requirements sections to all technology intro pages (#34613)
## Current Behavior

Technology introduction pages have inconsistent or missing version
requirements information. Some pages have no Requirements section,
others use ad-hoc formats (asides, bullet lists under Prerequisites),
and page titles follow different naming conventions ("Overview of the Nx
X Plugin", "Nx X Plugin Overview", "Introduction - X", etc.).

## Expected Behavior

Every technology introduction page now has a standardized Requirements
section with:
- A version support table using consistent semver range format
- Code-formatted package names in the `Package` column
- An intro sentence identifying the Nx plugin (e.g. "The `@nx/react`
plugin supports the following package versions.")
- A note linking to [code generation docs](/docs/features/generate-code)
for auto-installed packages
- Consistent **"X Plugin for Nx"** page title pattern across all intro
pages

Additional changes:
- Deleted the standalone Node.js/TypeScript compatibility page, inlining
its content into the respective plugin intro pages
- Created a proper introduction page for Angular Rsbuild (previously
linked directly to `createConfig` API reference)
- Added Requirements tables to Java, Gradle, and Maven pages with system
dependency versions
- Updated sidebar links and redirects for removed/moved pages
- Applied style guide fixes across all edited pages (removed "allows you
to", "easily", product possessives, etc.)

## Related Issue(s)

Fixes DOC-423
2026-02-26 10:42:51 -05:00
Leosvel Pérez Espinosa b1614d7504 feat(angular): add support for Angular v21.2 (#34592)
## Current Behavior

Nx doesn't support Angular v21.2.

## Expected Behavior

Nx should support Angular v21.2.
2026-02-26 10:23:53 -05:00
Caleb Ukle 0ae45cd445 docs(nx-plugin): document special schema options (#34615)
Co-authored-by: nx-cloud[bot] <71083854+nx-cloud[bot]@users.noreply.github.com>
Co-authored-by: barbados-clemens <barbados-clemens@users.noreply.github.com>
2026-02-26 17:56:54 +09:00
Jason Jean fcf4660389 fix(core): preserve nxCloud=skip in non-interactive CNW mode (#34616)
## Current Behavior

After #34580, `determineNxCloudV2()` returns `'skip'` in non-interactive
mode, but the caller remaps it to `nxCloud = 'yes'` with
`skipCloudConnect = true`. This causes `setupCI()` to run and generate
`.github/workflows/ci.yml` in new workspaces — which didn't happen
before.

This breaks the `extras.test.ts` e2e snapshot test because
`.github/workflows/ci.yml` now appears in the expanded default task
inputs.

## Expected Behavior

Keep `nxCloud = 'skip'` when the cloud choice is `'skip'`, which
prevents CI file generation in non-interactive mode. This restores the
behavior prior to #34580.

## Related Issue(s)

Fixes the `extras.test.ts` e2e snapshot failure introduced by #34580.
2026-02-25 23:02:58 -05:00
Louie Weng 221ea40462 chore(gradle): bump version to 0.1.13 (#34614)
<!-- Please make sure you have read the submission guidelines before
posting an PR -->
<!--
https://github.com/nrwl/nx/blob/master/CONTRIBUTING.md#-submitting-a-pr
-->

<!-- Please make sure that your commit message follows our format -->
<!-- Example: `fix(nx): must begin with lowercase` -->

<!-- If this is a particularly complex change or feature addition, you
can request a dedicated Nx release for this pull request branch. Mention
someone from the Nx team or the `@nrwl/nx-pipelines-reviewers` and they
will confirm if the PR warrants its own release for testing purposes,
and generate it for you if appropriate. -->

## Current Behavior
<!-- This is the behavior we have today -->

## Expected Behavior
<!-- This is the behavior we should expect with the changes in this PR
-->

Bump project graph plugin version.

## Related Issue(s)
<!-- Please link the issue being fixed so it gets closed when this is
merged. -->

Fixes #

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 21:50:07 +00:00
Leosvel Pérez Espinosa 12812dc994 cleanup(core): cache compiled glob sets to avoid redundant recompilation (#34602)
## Current Behavior

`build_glob_set` recompiles identical glob pattern sets on every call,
even when the same set of patterns has been compiled before.

## Expected Behavior

Compiled `NxGlobSet` instances are cached in a static `DashMap` keyed by
sorted glob strings. Repeated calls with the same patterns return a
shared `Arc<NxGlobSet>` instead of recompiling. Profiling `nx run-many
-t build lint test --parallel 8` in the Nx repo measured 95.6% cache hit
rate (9,758 of 10,202 calls) with only 444 unique pattern sets, reducing
hashing-phase CPU by ~40%.
2026-02-25 15:52:30 -05:00
Jack Hsu 4c3812f731 fix(nx-dev): move redirects from Next.js config to Netlify _redirects (#34612)
## Current Behavior

All 1,200+ redirect rules are processed by the Next.js serverless
function via the `redirects()` config in `next.config.js`. Every
redirect request requires a cold start of the serverless function, which
contributed to the 10-minute outage reported in DOC-415.

## Expected Behavior

Redirects are handled at the Netlify CDN edge via a plain `_redirects`
file, which is faster and doesn't depend on the Next.js serverless
function being healthy.

- Converted all redirect rules from `redirect-rules.js` and
`redirect-rules-docs-to-astro.js` into Netlify `_redirects` format
(1,231 rules)
- Expanded Next.js regex group patterns (e.g. `/(l|latest)/...`) into
individual Netlify rules since Netlify doesn't support regex
- Converted `:path*` wildcards to Netlify `*`/`:splat` syntax
- Rewrites (Astro docs proxy) remain in `next.config.js` as they require
server-side processing
- Original JS redirect files kept for reference (can be removed in
follow-up)

## Related Issue(s)

Fixes DOC-415
2026-02-25 15:49:44 -05:00
Leosvel Pérez Espinosa 098a830e5d fix(core): use scoped cache key for unresolved npm imports in TargetProjectLocator (#34605)
## Current Behavior

`TargetProjectLocator.findProjectFromImport` stores `null` for
unresolved imports using a bare `importExpr` key, but
`findNpmProjectFromImport` looks up cache entries using
`${packageName}__${dirPath}`. The key mismatch means repeated lookups
for the same import+directory re-run the full resolution waterfall
(typescript + require.resolve) instead of returning the cached `null`.

## Expected Behavior

Store `null` for unresolved imports using the same
`${packageName}__${dirPath}` key that `findNpmProjectFromImport` uses
for lookups. Repeated lookups for already-failed imports skip the
expensive resolution steps.

Also removes an unused cache write for builtin module imports as a minor
cleanup.
2026-02-25 15:47:54 -05:00
Jason Jean f31e7a75be fix(core): handle FORCE_COLOR=0 with picocolors (#34520)
## Current Behavior

After migrating from chalk to picocolors (#34305), `FORCE_COLOR=0` no
longer disables colors. picocolors checks `!!env.FORCE_COLOR`, and since
`!!"0"` is `true` in JavaScript, it treats `FORCE_COLOR=0` as "enable
colors."

This breaks CI environments and tools like Homebrew that set
`FORCE_COLOR=0` to get plain text output.

## Expected Behavior

`FORCE_COLOR=0` should disable ANSI color output, matching the previous
chalk behavior and the [FORCE_COLOR spec](https://force-color.org/).

## Related Issue(s)

Fixes #34387

Upstream issue filed:
https://github.com/alexeyraspopov/picocolors/issues/100
2026-02-25 15:47:24 -05:00
Louie Weng 09c44a637a fix(gradle): use globs for dependent task output files (#34590)
## Current Behavior

When processing Gradle tasks, Nx tracks dependent task output files by
recording individual file paths for each output. This can lead to
incorrect cache invalidation behavior since we are prefixing the paths
unnecessarily. We will therefore never match.

## Expected Behavior

Nx now consolidates dependent task output files using glob patterns
based on file extensions (e.g., **/*.jar, **/*.class). This focuses on
the types of files produced rather than their specific paths. The
approach groups all output files by extension and generates a single
glob pattern per extension, reducing the complexity of input tracking
while maintaining correctness.

## Related Issue(s)
<!-- Please link the issue being fixed so it gets closed when this is
merged. -->

Fixes #Q-247
2026-02-25 15:02:34 -05:00
Louie Weng dc81b8bbd6 fix(gradle): ensure that atomized task targets have dependsOn (#34611)
<!-- Please make sure you have read the submission guidelines before
posting an PR -->
<!--
https://github.com/nrwl/nx/blob/master/CONTRIBUTING.md#-submitting-a-pr
-->

<!-- Please make sure that your commit message follows our format -->
<!-- Example: `fix(nx): must begin with lowercase` -->

<!-- If this is a particularly complex change or feature addition, you
can request a dedicated Nx release for this pull request branch. Mention
someone from the Nx team or the `@nrwl/nx-pipelines-reviewers` and they
will confirm if the PR warrants its own release for testing purposes,
and generate it for you if appropriate. -->

## Current Behavior
<!-- This is the behavior we have today -->

## Expected Behavior
<!-- This is the behavior we should expect with the changes in this PR
-->

The dependsOn of atomized tasks should match the base non-atomized task.

## Related Issue(s)
<!-- Please link the issue being fixed so it gets closed when this is
merged. -->

Fixes Q-174
2026-02-25 19:03:42 +00:00
Jack Hsu f7e46e33e9 feat(core): add explicit cloud opt-out to CNW (#34580)
## Current Behavior

The CNW cloud prompt is locked to auto-select deferred connection
(CLOUD-4255), always generating a short URL but never writing nxCloudId
to nx.json. Users have no explicit choice.


## Expected Behavior

The cloud prompt now offers three explicit choices:
- Yes: connect now, generate nxCloudId in nx.json, show strong
completion message
- Skip for now: deferred connection (no nxCloudId), still show short URL
and update README
- No: full opt-out, set neverConnectToCloud: true in nx.json, no URL, no
README update, no cloud messaging

**CLI args:** `--nxCloud=skip`, `--nxCloud=never` (new),
`--nxCloud=yes`. Non-interactive defaults to skip.

Closes CLOUD-4242
2026-02-25 13:56:03 -05:00
Jason Jean 5d64f726d6 chore(core): add tests for large directory file watching (#34601)
## Current Behavior

PR #34523 fixed the macOS file watcher issue (#34522) but did not
include comprehensive test coverage.

## Expected Behavior

Tests should verify that the fix works correctly and catch any future
regressions.

## Related Issue(s)

Adds test coverage for #34523 and #34522

---

## Changes

**TypeScript integration test**
(`packages/nx/src/native/tests/watcher.spec.ts`):

Added **"should detect file changes in large directory structures"** - a
comprehensive integration test that:
1. Creates 10,000+ directories simulating a monorepo-scale workspace
2. Starts a real `Watcher` instance
3. Creates and modifies files deep in the directory tree
4. Verifies that file change events are actually delivered

This test validates the actual behavior users care about - that file
watching works reliably in large repos - rather than testing
implementation details. It would catch any regression where events fail
to be delivered at scale.

## Testing

TypeScript integration test validates the actual bug fix - that file
events are delivered reliably in large directory structures with 10,000+
directories.
2026-02-25 13:36:48 -05:00
Nikola Kalinov 1e1a8a7a40 fix(vite): isPreview=true for Vite Preview server (#34597)
<!-- Please make sure you have read the submission guidelines before
posting an PR -->
<!--
https://github.com/nrwl/nx/blob/master/CONTRIBUTING.md#-submitting-a-pr
-->

<!-- Please make sure that your commit message follows our format -->
<!-- Example: `fix(nx): must begin with lowercase` -->

<!-- If this is a particularly complex change or feature addition, you
can request a dedicated Nx release for this pull request branch. Mention
someone from the Nx team or the `@nrwl/nx-pipelines-reviewers` and they
will confirm if the PR warrants its own release for testing purposes,
and generate it for you if appropriate. -->

## Current Behavior
Given the following Vite config:
```ts
import { defineConfig } from 'vite';

export default defineConfig((config) => {
  console.log(config);
  return {};
});
```

`npx nx preview` logs:
```ts
{
  mode: 'development',
  command: 'build',
  isSsrBuild: false,
  isPreview: false
}
```

## Expected Behavior
`npx nx preview` should log:
```ts
{
  mode: 'development',
  command: 'build',
  isSsrBuild: false,
  isPreview: true
}
```

## Related Issue(s)
https://github.com/vitejs/vite/issues/15694

Fixes #
2026-02-25 13:27:05 -05:00
Leosvel Pérez Espinosa 872b9c9045 fix(core): remove unused getTerminalOutput from BatchProcess (#34604)
## Current Behavior

`BatchProcess` accumulates all stdout/stderr output in
`terminalOutputChunks` and exposes it via `getTerminalOutput()`, but
nothing ever calls `getTerminalOutput()`. The accumulated strings are
unique allocations (created via `chunk.toString()`), not shared with the
output callbacks or `process.stdout.write`.

For verbose batched tasks (e.g., Maven/Gradle with hundreds of tasks),
this can hold tens to hundreds of MB for the entire batch duration.

## Expected Behavior

Remove the dead accumulation code. stdout/stderr chunks are still
forwarded to `process.stdout`/`process.stderr` and output callbacks as
before — only the unused storage is removed.

---------

Co-authored-by: nx-cloud[bot] <71083854+nx-cloud[bot]@users.noreply.github.com>
2026-02-25 12:39:17 -05:00
Jack Hsu 1e3f8e00f7 fix(js): remove redundant vite.config.ts generation for vitest projects (#34603)
## Current Behavior

When generating a library with vitest as the test runner and a non-vite
bundler (e.g. `tsc`), the js library generator creates two config files:
- `vitest.config.mts` (from the vitest `configurationGenerator`) with
`root: __dirname`
- `vite.config.ts` (from a second `createOrEditViteConfig` call) with
`root: import.meta.dirname`

The redundant `vite.config.ts` uses ESM-only `import.meta.dirname`
syntax, which causes TS1470 when the project targets CommonJS output:

```
vite.config.ts:5:9 - error TS1470: The 'import.meta' meta-property is not allowed in files which will build into CommonJS output.

5   root: import.meta.dirname,
          ~~~~~~~~~~~
```

## Expected Behavior

Only `vitest.config.mts` should be generated. The vitest
`configurationGenerator` already handles creating the correct config
file with `root: __dirname`. The second `createOrEditViteConfig` call
from `@nx/vite` was redundant and produced the conflicting file.

## Related Issue(s)

Fixes #34399
2026-02-25 12:27:22 -05:00
Philip Fulcher 919907cf0f docs(nx-dev): add foundations article (#34599) 2026-02-25 11:23:22 -06:00
Charlie Croom d5cd6a1a56 fix(core): use recursive FSEvents on macOS instead of non-recursive kqueue (#34523)
## Current Behavior

Since Nx 22.5.0, the daemon's native file watcher silently drops all
file change events on macOS in large monorepos (~5,250+ watched
directories). `nx watch`, `nx serve`, and any daemon-dependent file
watching is broken.

The root cause is that #34329 switched all watched paths to
`WatchedPath::non_recursive()`. On macOS, the `notify` crate uses
**kqueue** for non-recursive watches instead of **FSEvents**. kqueue
silently fails at scale due to vnode table pressure (`kern.num_vnodes ==
kern.maxvnodes`), causing the daemon to never detect file changes.

This is a **scale-dependent** bug: it works fine in small workspaces
(~30 directories) but breaks silently in large ones.

| | **Nx 22.4.5** | **Nx 22.5.0+** |
|---|---|---|
| **Small repo (~30 dirs)** | Works (FSEvents) | Works (~30 kqueue
watches) |
| **Large repo (~5,250+ dirs)** | Works (FSEvents) | **Broken** (kqueue
silently drops all events) |

## Expected Behavior

The macOS file watcher should detect file creates, modifications, and
deletions at any scale, matching the behavior of Nx 22.4.x.

## Fix

Use platform-conditional watch modes:
- **macOS:** Single recursive watch on the workspace root (uses FSEvents
natively)
- **Linux/Windows:** Non-recursive per-directory watches (preserves the
#33781 inotify fix)

On macOS, FSEvents handles recursive watching from a single root path,
so directory enumeration and dynamic registration are skipped entirely.
This also improves daemon startup time on macOS from ~10 minutes to <1
second in a 354-project monorepo.

### What changed in `watcher.rs`

1. **Initial pathset:** On macOS, watch only the root directory
recursively via FSEvents instead of enumerating all directories for
non-recursive kqueue watches.
2. **Dynamic directory registration (`on_action`):** Wrapped in
`#[cfg(not(target_os = "macos"))]` since FSEvents already watches the
full tree.

Linux/Windows behavior is completely unchanged.

### Why the event filter is fine as-is

We verified that with recursive FSEvents watches, macOS emits specific
`FileEventKind` variants (`Create(File)`, `Modify(Data(Content))`,
`Remove(File)`, `Modify(Name(Any))`) that the current
`watch_filterer.rs` already handles correctly. Zero events were rejected
by the catch-all. The `Modify(Any)` / `Create(Any)` variants are kqueue
artifacts that are not needed with FSEvents.

### Why kqueue fails silently

Apple's [File System Events Programming
Guide](https://developer.apple.com/library/archive/documentation/Darwin/Conceptual/FSEvents_ProgGuide/KernelQueues/KernelQueues.html)
explicitly recommends FSEvents over kqueue for large hierarchies: *"If
you are monitoring a large hierarchy of content, you should use file
system events instead."* kqueue requires `open(path, O_EVTONLY)` per
watched directory. Under vnode table pressure, the kernel recycles
vnodes with kqueue watches attached without notifying the watcher. There
is no error, no partial delivery, and no diagnostic signal.

## Tested on

- macOS 26.3 (Tahoe), Apple Silicon (arm64), APFS
- 354-project pnpm monorepo (~19,865 non-ignored directories)
- Verified: file modifications, file creates, and file deletes all
detected
- Daemon init time: ~10 min (with enumeration) -> <1s (with root-only
FSEvents watch)

## Related Issue(s)

Fixes #34522

Co-authored-by: Amp <amp@ampcode.com>
2026-02-25 09:45:31 -05:00
Caleb Ukle 700c98fcaf fix(nx-dev): correct interpolate sub command for cli reference (#34585)
also adding e2e for command hierarchy

<img width="823" height="362" alt="image"
src="https://github.com/user-attachments/assets/3db98945-4221-4bf3-8b92-9d5b25eb2444"
/>

<img width="778" height="349" alt="image"
src="https://github.com/user-attachments/assets/432ebd61-c34e-40f5-b95a-f8d73c682da4"
/>


![wm_2026-02-24T14-11-07@2x](https://github.com/user-attachments/assets/a3d635a3-b928-4ff2-a147-28f0873d26c2)
2026-02-25 14:30:34 +00:00
Colum Ferry df9eb0bf10 fix(release): add null-safe fallback for version in createGitTagValues (#34598)
## Current Behavior

When nx release runs with docker-configured projects (either via
explicit config or
@nx/docker plugin inference), git tags are created with the literal
string {version}
instead of the actual version number (e.g., v{version} instead of
v1.0.6, or
  app-3@{version} instead of app-3@1.0.0).

  This happens because:

1. If ANY project in a release group has docker config,
preferDockerVersion is auto-set to
  true for the ENTIRE group
2. createGitTagValues() then blindly selects
projectVersionData.dockerVersion, which is
  null for non-docker projects (or projects with no changes)
3. The interpolate() function receives null for {version} and returns
the literal
  placeholder unchanged

Commit messages are unaffected because createCommitMessageValues() only
uses newVersion and
already guards against null. The changelog code (changelog.ts:1117-1121)
also already has
  the correct null-safe pattern.

 ## Expected Behavior

When preferDockerVersion is true but dockerVersion is null, git tags
should fall back to
using newVersion instead of producing literal {version} placeholders.
When both versions
  are null, no tag should be created.

For mixed release groups (some projects have docker config, some don't),
the auto-enable
logic should use 'both' mode instead of true, which already has proper
null-safe checks for
   each version type.

 ## Changes

- shared.ts: Added null-safe fallback (??) in createGitTagValues() for
both independent and
fixed group code paths, plus a guard to skip tag creation when both
versions are null
- config.ts: Refined auto-enable logic to check whether ALL or only SOME
projects have
  docker config — mixed groups now get 'both' mode instead of true
- shared.spec.ts: Added 5 test cases covering null version fallback
scenarios for fixed
groups, independent groups, both-null, reverse fallback, and mixed
groups

 ## Related Issue(s)

  Fixes #34382
  Fixes #33890
  Fixes #34391
2026-02-25 09:10:01 -05:00
MaxKless 4e55f9aa32 docs(misc): update nx download stats (#34596)
## Current Behavior
The "7. Thriving Community" section on
`nx.dev/docs/guides/adopting-nx/from-turborepo` displayed an outdated Nx
download statistic (~5 million downloads per week).

## Expected Behavior
The "7. Thriving Community" section on
`nx.dev/docs/guides/adopting-nx/from-turborepo` now reflects the latest
Nx download statistic (~9 million downloads per week).

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: MaxKless <MaxKless@users.noreply.github.com>
2026-02-25 08:26:01 -05:00
Jack Hsu 127255aa96 fix(bundling): fix regression on process.env usage for webpack (#34583)
When we optimized the `process.env` values to not embed the full object
unnecessarily, we also regressed in cases where users do use
`process.env` instead of `process.env["NX_PUBLIC_FOO"]`.

## Current behavior

Users cannot use `process.env` and must access each key individuall.
Although the serializing the full object can bloat bundle sizes, we also
don't want to break existing apps unnecessarily.

## Expected behavior

Existing apps should continue to work as usual.

## Related issues

Fixes #34279
2026-02-25 08:16:05 -05:00
Kai Gritun b46de60cd9 fix(js): guard against undefined closest node in rehoistNodes (#34347)
## Current Behavior

When running `@nx/js:prune-lockfile` on a monorepo with transitive
dependencies that have multiple versions where neither version is
reachable from a direct dependency in package.json, the executor throws:

```
NX   An error occurred while creating pruned lockfile

Original error: Cannot read properties of undefined (reading 'name')

TypeError: Cannot read properties of undefined (reading 'name')
    at switchNodeToHoisted (node_modules/nx/src/plugins/js/lock-file/project-graph-pruning.js:165:31)
```

## Expected Behavior

The lockfile pruning should complete without crashing, even when some
transitive dependencies cannot be traced back to a direct dependency.

## Root Cause

In `rehoistNodes()`, when there are multiple nested nodes for a package,
the code finds the "closest" node by computing `pathLengthToIncoming()`
for each. However, when none of the nested nodes have a path to any
direct dependency in package.json, `pathLengthToIncoming()` returns
`undefined` for all of them. Since `undefined < Infinity` is `false` in
JavaScript, `closest` remains `undefined`, and then
`switchNodeToHoisted(undefined, ...)` crashes.

## Fix

Add a guard to only call `switchNodeToHoisted()` when a closest node was
actually found:

```typescript
if (closest) {
  switchNodeToHoisted(closest, builder, invBuilder);
}
```

This allows the pruning to continue - the nested nodes simply won't be
rehoisted if no closest node can be determined.

## Related Issue

Fixes #34322

## Test Added

Added a unit test that verifies `rehoistNodes()` doesn't crash when
nested nodes have no path to package.json dependencies.
2026-02-25 13:28:19 +01:00
Tomas Ptacek 736551590a fix(angular-rspack): exclude .json files from JS/TS regex patterns (#34195)
## Current Behavior

When importing a `package.json` file in an Angular application built
with `@nx/angular-rspack`, the build fails with a Babel syntax error if
the `package.json` contains `@angular/*` dependencies:

```
SyntaxError: /path/to/package.json: Missing semicolon. (2:10)

  1 | {
> 2 |     "name": "@org/app",
    |           ^
  3 |     "version": "4.0.2",
  4 |     "dependencies": {
  5 |         "@angular/platform-browser": "20.3.7",
```

This happens because the `JS_ALL_EXT_REGEX` pattern
`/\.[cm]?(js)[^x]?\??/` incorrectly matches `.json` files. When the JSON
file content contains `@angular` strings, the
`angular-partial-transform-loader` attempts to process it through Babel,
which fails because JSON is not valid JavaScript.

**Root cause:** The regex `[^x]?` (optional character that is NOT 'x')
allows `.json` to match because 'o' is not 'x'.

## Expected Behavior
- `.json` files should NOT match `JS_ALL_EXT_REGEX` or
`TS_ALL_EXT_REGEX`
- Importing `package.json` in Angular applications should work correctly
- All existing matches for `.js`, `.jsx`, `.mjs`, `.cjs` (and TypeScript
equivalents) should continue to work

## Related Issue(s)
https://github.com/nrwl/nx/issues/32649
2026-02-25 09:55:15 +00:00
MaxKless e031d024ef chore(repo): update @nx/graph to 1.0.4 (#34558) 2026-02-25 18:27:42 +09:00
Jason Jean d042483a3f chore(gradle): clean up project.json configurations (#34587)
## Current Behavior

The Gradle projects have redundant and inconsistent project.json
configurations:
- `batch-runner` has its own project.json with duplicate targets
- `project-graph` has duplicate test/lint/format targets
- Implicit dependency syntax is inconsistent between projects
- e2e project has unnecessary implicitDependencies

## Expected Behavior

Cleaner, more maintainable project structure:
- Consolidated batch-runner configuration into parent project
- Removed duplicate targets from project-graph
- Consistent implicit dependency syntax using project name format
(`:project-name`)
- Streamlined e2e project configuration

## Related Issue(s)

N/A - Internal cleanup
Closes Q-173

---------

Co-authored-by: nx-cloud[bot] <71083854+nx-cloud[bot]@users.noreply.github.com>
Co-authored-by: FrozenPandaz <FrozenPandaz@users.noreply.github.com>
Co-authored-by: Louie Weng <56288712+lourw@users.noreply.github.com>
2026-02-24 22:50:08 -05:00
Berend de Boer 39f252df97 docs(misc): add link to new nx-knip plugin (#34011)
This allows you to run knip against your typescript and javascript
projects.

Co-authored-by: Jack Hsu <jack.hsu@gmail.com>
2026-02-24 22:31:27 -05:00
Aude Planchamp 3f70561586 docs(misc): incorrect tsconfig inheritance described in TypeScript project references documentation (#34124)
## Current Behavior

Documentation bugfix on

https://nx.dev/docs/concepts/typescript-project-linking#set-up-typescript-project-references

In the section about setting up TypeScript project references, the
documentation currently states:

"Each project's tsconfig.lib.json file extends the project's
tsconfig.json file and adds references to the tsconfig.lib.json files of
project dependencies."

## Expected Behavior

In a standard Nx workspace configuration, tsconfig.lib.json extends the
workspace-level tsconfig.base.json, not the project-level tsconfig.json
(and the example provided just after is correct).

Suggested correction:

"Each project's tsconfig.lib.json file extends the workspace
tsconfig.base.json file and adds references to the tsconfig.lib.json
files of project dependencies."

## Related Issue(s)

Fixes  #34118

---------

Co-authored-by: Aude Planchamp <aude.planchamp@ekino.com>
Co-authored-by: graphite-app[bot] <96075541+graphite-app[bot]@users.noreply.github.com>
Co-authored-by: Jack Hsu <jack.hsu@gmail.com>
2026-02-24 22:31:13 -05:00
Miguel b72a203ed7 fix(release): allow null values in schema of dockerVersion (#34171)
<!-- Please make sure you have read the submission guidelines before
posting an PR -->
<!--
https://github.com/nrwl/nx/blob/master/CONTRIBUTING.md#-submitting-a-pr
-->

<!-- Please make sure that your commit message follows our format -->
<!-- Example: `fix(nx): must begin with lowercase` -->

<!-- If this is a particularly complex change or feature addition, you
can request a dedicated Nx release for this pull request branch. Mention
someone from the Nx team or the `@nrwl/nx-pipelines-reviewers` and they
will confirm if the PR warrants its own release for testing purposes,
and generate it for you if appropriate. -->

## Current Behavior
<!-- This is the behavior we have today -->

TS file documents `projectVersionData.dockerVersion` as having type
`string | undefined`. However, code behaves differently. For instance,
[here](https://github.com/nrwl/nx/blob/e57848cea748b85f17e5fc704b901975a8424c4d/packages/nx/src/command-line/release/version/release-group-processor.ts#L128)
and
[here](https://github.com/nrwl/nx/blob/e57848cea748b85f17e5fc704b901975a8424c4d/packages/nx/src/command-line/release/utils/shared.ts#L294)
it is setting as and comparing against `null`.

## Expected Behavior
<!-- This is the behavior we should expect with the changes in this PR
-->

Schema has value that code sets (`null`)

## Related Issue(s)
<!-- Please link the issue being fixed so it gets closed when this is
merged. -->

Fixes https://github.com/nrwl/nx/issues/34172
2026-02-24 22:30:24 -05:00
Craigory Coppola 1ecf0fb6a7 fix(core): reject pending promises directly when plugin worker exits unexpectedly (#34588)
When a plugin worker process exits unexpectedly, the exit handler
previously sent synthetic `loadResult` messages to all pending response
handlers. If any handler was waiting for a different result type (e.g.
`createNodesResult`), the type validation would reject with a confusing
"Expected createNodesResult, got loadResult" error instead of surfacing
the actual cause.

Split response handlers into `onMessage` / `onError` callbacks so the
exit handler can reject each pending promise directly with a clear
"Plugin worker exited unexpectedly" error.

Also use unique transaction IDs for `load` messages (via `generateTxId`)
to avoid potential handler overwrites during worker restarts.

Fixes #34564
2026-02-24 17:44:40 -05:00
Jason Jean c743313078 chore(repo): update nx to 22.6.0-beta.3 (#34579)
Updating Nx from 22.6.0-beta.2 to 22.6.0-beta.3
2026-02-24 17:36:31 -05:00
Jason Jean 1bbe936513 chore(repo): disable CI continuous assignment (#34578)
## Summary

Testing CI behavior with continuous assignment disabled and cache bust
set to 4.

This is part of investigating flakiness potentially related to
continuous assignment in CI.

## Test plan

- Monitor CI execution behavior
- Compare with other test branches (bust=2, bust=3)
2026-02-24 17:34:24 -05:00
Jack Hsu c966e20746 docs(misc): dedupe and clean up getting started pages (#34521)
## Current Behavior

Documentation pages across Getting Started, How Nx Works, and Platform
Features sections contain:

1. Duplicated content — mental-model.mdoc has a ~70-line caching section
and a ~20-line DTE section that are near-verbatim
copies of how-caching-works.mdoc and distribute-task-execution.mdoc
respectively. remote-cache.mdoc re-explains local caching
 in its intro instead of linking to the canonical page.
2. Missing cross-reference links — Key concepts like "affected command",
"remote cache", "project graph", and "task pipeline
configuration" are mentioned without linking to their dedicated pages.
3. Style guide violations — Trust-undermining words ("simply", "just",
"straightforward"), anti-AI phrases ("Let's take",
"Whether you're..."), product possessives ("Nx's"), customer perspective
issues ("allows you to"), and em dashes appear
across Getting Started and How Nx Works pages.

## Expected Behavior

1. Content consolidation — mental-model.mdoc is trimmed by ~85 lines,
keeping the concept + images and linking to dedicated
pages for details. remote-cache.mdoc intro references the canonical
caching page. publish-conformance-rules-to-nx-cloud.mdoc
deduplicates its intro. maintain-typescript-monorepos.mdoc shortens its
inferred tasks re-explanation.
2. Cross-reference links added — First-mention links for affected,
remote cache, computation caching, task pipeline
configuration (in mental-model) and project graph (in self-healing-ci).
3. Style guide compliance — 18 fixes across 10 Getting Started and How
Nx Works pages, removing banned phrases and aligning
with the new STYLE_GUIDE.md.
4. Sidebar improvements — Cache Task Results added after Run Tasks in
Platform Features; Maintain TypeScript Monorepos moved
to first in KB > TypeScript.

## Pages changed
```
┌─────────────────┬─────────────────────────────────────────────────────────────────────────────────────────────────┐
│     Section     │                                              Pages                                              │
├─────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────────┤
│ Getting Started │ intro, index, nx-cloud, ai-setup, start-with-existing-project                                   │
├─────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────────┤
│ How Nx Works    │ mental-model, how-caching-works, task-pipeline-configuration, nx-plugins, nx-daemon             │
├─────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────────┤
│ Features        │ remote-cache, self-healing-ci, maintain-typescript-monorepos, cache-task-results (sidebar only) │
├─────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────────┤
│ Enterprise      │ publish-conformance-rules-to-nx-cloud                                                           │
└─────────────────┴─────────────────────────────────────────────────────────────────────────────────────────────────┘
```
2026-02-24 16:42:31 -05:00
Miroslav Jonaš f42976f852 fix(repo): remove chalk from e2e tests (#34570)
<!-- Please make sure you have read the submission guidelines before
posting an PR -->
<!--
https://github.com/nrwl/nx/blob/master/CONTRIBUTING.md#-submitting-a-pr
-->

<!-- Please make sure that your commit message follows our format -->
<!-- Example: `fix(nx): must begin with lowercase` -->

<!-- If this is a particularly complex change or feature addition, you
can request a dedicated Nx release for this pull request branch. Mention
someone from the Nx team or the `@nrwl/nx-pipelines-reviewers` and they
will confirm if the PR warrants its own release for testing purposes,
and generate it for you if appropriate. -->

## Current Behavior
<!-- This is the behavior we have today -->

## Expected Behavior
<!-- This is the behavior we should expect with the changes in this PR
-->

## Related Issue(s)
<!-- Please link the issue being fixed so it gets closed when this is
merged. -->

Fixes #
2026-02-24 15:39:27 -05:00
Leosvel Pérez Espinosa 6d90572257 fix(core): show the correct status for stopped continuous tasks (#34226)
## Current Behavior

### 1. Continuous tasks missing from `postTasksExecution` hook

When running continuous tasks (e.g., `nx serve app`) and stopping them
with Ctrl+C, the `postTasksExecution` lifecycle hook does not include
them in `taskResults`. This breaks plugins that rely on post-run
statistics (e.g., uploading task stats to DataDog).

### 2. Confusing TUI status when sibling continuous task exits

When multiple continuous tasks run together and one exits unexpectedly,
its sibling is marked as "failed" even though it was intentionally
terminated/stopped by the task orchestrator.

## Expected Behavior

1. All tasks, including continuous ones, are included in `taskResults`
for the `postTasksExecution` hook
2. Continuous tasks that are intentionally stopped (because dependent
tasks completed or during graceful shutdown) report as `success` with
`Stopped` display status
3. Continuous tasks that exit unexpectedly (crash) report as `failure`
4. TUI summary shows correct status: success when all tasks completed
successfully, square icon for stopped tasks

## Related Issue(s)

Fixes https://github.com/nrwl/nx/issues/33561

Supersedes:

- https://github.com/nrwl/nx/pull/33562
- https://github.com/nrwl/nx/pull/34132
2026-02-24 13:48:52 -05:00
Rares Matei f84ec34cbd chore(repo): enable signal file writing (#34572)
Add NX_CLOUD_IO_TRACING_DIRECTORY environment variable.

<!-- Please make sure you have read the submission guidelines before
posting an PR -->
<!--
https://github.com/nrwl/nx/blob/master/CONTRIBUTING.md#-submitting-a-pr
-->

<!-- Please make sure that your commit message follows our format -->
<!-- Example: `fix(nx): must begin with lowercase` -->

<!-- If this is a particularly complex change or feature addition, you
can request a dedicated Nx release for this pull request branch. Mention
someone from the Nx team or the `@nrwl/nx-pipelines-reviewers` and they
will confirm if the PR warrants its own release for testing purposes,
and generate it for you if appropriate. -->

## Current Behavior
<!-- This is the behavior we have today -->

## Expected Behavior
<!-- This is the behavior we should expect with the changes in this PR
-->

## Related Issue(s)
<!-- Please link the issue being fixed so it gets closed when this is
merged. -->

Fixes Q-245
2026-02-24 17:34:51 +00:00
Juri 566a370375 docs(core): move synthetic monorepos back to "How Nx Works" sidebar section 2026-02-24 18:02:29 +01:00
Juri Strumpflohner 4c1dd1e5ed docs(core): add synthetic monorepos page (#34565)
## Current Behavior

No documentation exists explaining the concept of synthetic monorepos —
how they bridge polyrepo and monorepo setups by connecting separate
repositories into a unified dependency graph.


https://deploy-preview-34565--nx-docs.netlify.app/docs/concepts/synthetic-monorepos

## Expected Behavior

New concept page under "How Nx Works" that explains:
- What synthetic monorepos are (unified graph across separate repos
without moving code)
- Why they matter for humans (visibility, cross-repo coordination) and
AI agents (seeing beyond repo boundaries)
- What they provide (cross-repo graph, actionable tooling, AI agent
enablement)
- How they serve as a gradual entry point toward deeper monorepo
adoption

## Related Issue(s)

N/A — new documentation page based on existing content from webinars and
internal knowledge.

---------

Co-authored-by: nx-cloud[bot] <71083854+nx-cloud[bot]@users.noreply.github.com>
Co-authored-by: juristr <juristr@users.noreply.github.com>
2026-02-24 16:51:20 +01:00
Loëck Vézien 8f64e844c9 feat(core): add yarn berry catalog support (#34552)
## Current Behavior

The Nx catalog system (`catalog:` protocol) only supports pnpm
workspaces. Yarn Berry (v4+) introduced [catalog
support](https://yarnpkg.com/features/catalogs) in `.yarnrc.yml`, but Nx
does not recognize `catalog:` references in Yarn workspaces. This means
Yarn Berry users cannot benefit from Nx's catalog-aware dependency
resolution, validation, or version updating.

## Expected Behavior

Nx should support Yarn Berry's `catalog:` protocol, just like it does
for pnpm. With this PR:

- `catalog:` and `catalog:<name>` references in `package.json`
dependencies are correctly resolved against `.yarnrc.yml` definitions
for Yarn Berry workspaces
- Validation provides helpful error messages with suggestions (missing
catalog, missing package, duplicate default definitions)
- Catalog versions can be updated programmatically via
`updateCatalogVersions`
- The `CatalogManager` interface is generalized with
`getCatalogDefinitionFilePaths()` and `CatalogDefinitions` to support
multiple package managers cleanly

### Changes

- **`YarnCatalogManager`** — New manager that reads `catalog:` /
`catalogs:` from `.yarnrc.yml`, mirroring the pnpm implementation with
Yarn-specific config paths and error messages
- **`yarn-workspace.ts`** — Type definitions for Yarn's `.yarnrc.yml`
catalog structure (`YarnWorkspaceYaml`, `YarnCatalogEntry`)
- **`manager-factory.ts`** — Registers `YarnCatalogManager` for `yarn`
package manager
- **`manager.ts`** — Adds `getCatalogDefinitionFilePaths()` to the
`CatalogManager` interface; moves `formatCatalogError` here from
`types.ts` (runtime function doesn't belong with type-only exports)
- **`types.ts`** — Adds generic `CatalogDefinitions` interface so
consumers don't need to depend on package-manager-specific types
- **`pnpm-manager.ts`** — Implements the new
`getCatalogDefinitionFilePaths()` method; import cleanup
- **592-line test suite** covering parsing, resolution, validation
(named catalogs, default catalog, dual-definition errors, missing
catalogs/packages with suggestions), and `updateCatalogVersions`

### Context

I'm currently applying a patch on the compiled `@nx/devkit` package in
my Yarn Berry project to get catalog support while waiting for upstream
support:

<details>
<summary>Current workaround patch on <code>@nx/devkit</code></summary>

```diff
diff --git a/src/utils/catalog/manager-factory.js b/src/utils/catalog/manager-factory.js
index 6216749..d46e242 100644
--- a/src/utils/catalog/manager-factory.js
+++ b/src/utils/catalog/manager-factory.js
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
 exports.getCatalogManager = getCatalogManager;
 const devkit_exports_1 = require("nx/src/devkit-exports");
 const pnpm_manager_1 = require("./pnpm-manager");
+const yarn_manager_1 = require("./yarn-manager");
 /**
  * Factory function to get the appropriate catalog manager based on the package manager
  */
@@ -11,6 +12,8 @@ function getCatalogManager(workspaceRoot) {
     switch (packageManager) {
         case 'pnpm':
             return new pnpm_manager_1.PnpmCatalogManager();
+        case 'yarn':
+            return new yarn_manager_1.YarnCatalogManager();
         default:
             return null;
     }
diff --git a/src/utils/catalog/yarn-manager.js b/src/utils/catalog/yarn-manager.js
new file mode 100644
index 0000000..048fdec
--- /dev/null
+++ b/src/utils/catalog/yarn-manager.js
@@ -0,0 +1,102 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+exports.YarnCatalogManager = void 0;
+const node_fs_1 = require("node:fs");
+const node_path_1 = require("node:path");
+const devkit_exports_1 = require("nx/src/devkit-exports");
+const devkit_internals_1 = require("nx/src/devkit-internals");
+class YarnCatalogManager {
+    constructor() {
+        this.name = 'yarn';
+        this.catalogProtocol = 'catalog:';
+    }
+    isCatalogReference(version) {
+        return version.startsWith(this.catalogProtocol);
+    }
+    parseCatalogReference(version) {
+        if (!this.isCatalogReference(version)) { return null; }
+        return { catalogName: undefined, isDefaultCatalog: true };
+    }
+    getCatalogDefinitions(treeOrRoot) {
+        if (typeof treeOrRoot === 'string') {
+            const p = (0, node_path_1.join)(treeOrRoot, '.yarnrc.yml');
+            if (!(0, node_fs_1.existsSync)(p)) { return null; }
+            return readYamlFileFromFs(p);
+        } else {
+            if (!treeOrRoot.exists('.yarnrc.yml')) { return null; }
+            return readYamlFileFromTree(treeOrRoot, '.yarnrc.yml');
+        }
+    }
+    resolveCatalogReference(treeOrRoot, packageName, version) {
+        if (!this.parseCatalogReference(version)) { return null; }
+        const config = this.getCatalogDefinitions(treeOrRoot);
+        if (!config || !config.catalog) { return null; }
+        return config.catalog[packageName] || null;
+    }
+    validateCatalogReference(treeOrRoot, packageName, version) {
+        if (!this.parseCatalogReference(version)) {
+            throw new Error(`Invalid catalog reference: "${version}"`);
+        }
+        const config = this.getCatalogDefinitions(treeOrRoot);
+        if (!config) { throw new Error('No .yarnrc.yml found'); }
+        if (!config.catalog) { throw new Error('No catalog in .yarnrc.yml'); }
+        if (!config.catalog[packageName]) {
+            throw new Error(`"${packageName}" not in .yarnrc.yml catalog`);
+        }
+    }
+    updateCatalogVersions() {}
+}
+exports.YarnCatalogManager = YarnCatalogManager;
+function readYamlFileFromFs(path) {
+    try { return (0, devkit_internals_1.readYamlFile)(path); }
+    catch (e) {
+        devkit_exports_1.output.warn({ title: 'Unable to parse .yarnrc.yml', bodyLines: [e.toString()] });
+        return null;
+    }
+}
+function readYamlFileFromTree(tree, path) {
+    const content = tree.read(path, 'utf-8');
+    const { load } = require('@zkochan/js-yaml');
+    try { return load(content, { filename: path }); }
+    catch (e) {
+        devkit_exports_1.output.warn({ title: 'Unable to parse .yarnrc.yml', bodyLines: [e.toString()] });
+        return null;
+    }
+}
```

</details>

This PR replaces that workaround with a proper TypeScript implementation
including full test coverage, named catalog support, helpful error
messages, and `updateCatalogVersions` support.

## Related Issue(s)

<!-- No existing issue found for Yarn Berry catalog support — this PR
introduces the feature -->
2026-02-24 10:34:40 -05:00
Jack Hsu 7f7bba633d fix(bundling): add docs link to generatePackageJson error message (#34562)
## Current Behavior

When users hit the `generatePackageJson: true` error with TS Solution
Setup, the error tells them to "unset the option" but gives no guidance
on the replacement workflow.

## Expected Behavior

The error message now includes a link to the pruning guide at
https://nx.dev/docs/technologies/node/guides/deploying-node-projects so
users can immediately find the migration steps.

## Related Issue(s)

Related #30146
2026-02-24 08:31:57 -05:00
Colum Ferry 1a15ea183a fix(js): use per-invocation cache in TS plugin to fix NX_ISOLATE_PLUGINS=false (#34566)
When plugin isolation is off, concurrent createNodesV2 invocations share
the same module instance. The module-level mutable `cache` variable
caused
invocation A's `finally` block to null it out while invocation B was
still
reading from it, resulting in "Cannot read properties of null (reading
'configContexts')".

Replace the shared mutable `cache` with a Symbol-keyed Map so each
invocation gets its own isolated cache. The tsconfig disk cache is
shared
across invocations with an idempotent initialization guard.

CLOSES NXC-3971
2026-02-24 12:51:54 +00:00
Juri Strumpflohner 8c0600225a docs(nx-dev): add 'A Monorepo Is NOT a Monolith' blog post (#34567)
## Summary
- Updated version of the classic "Misconceptions about Monorepos"
article
- New sections on AI compatibility, scaling strategies (affected,
caching, distribution, atomization), and `@nx/owners`
- Custom SVG diagrams for project graph illustrations (replacing old
Medium images)
- Authors: Victor Savkin, Juri Strumpflohner

## Test plan
- [ ] Verify blog post renders correctly on preview
- [ ] Check all images load (SVGs + avif)
- [ ] Verify internal doc links resolve
- [ ] Check TOC renders properly

---------

Co-authored-by: nx-cloud[bot] <71083854+nx-cloud[bot]@users.noreply.github.com>
Co-authored-by: juristr <juristr@users.noreply.github.com>
2026-02-24 13:46:52 +01:00
MaxKless 832355081b feat(core): add passthrough for nx-cloud apply-locally command (#34557)
## Current Behavior
folks had to type in `nx-cloud apply-locally`

## Expected Behavior
now `nx apply-locally` works

---------

Co-authored-by: nx-cloud[bot] <71083854+nx-cloud[bot]@users.noreply.github.com>
2026-02-24 19:42:41 +09:00
MaxKless 1081c320ab feat(core): add --json flag for better AX to nx list (#34551)
### Current Behavior

nx list <plugin> shows generator/executor names and descriptions in text
format only. It does not show where the plugin or its
generators/executors are located on disk, and there is no
machine-readable output option.

  ### Expected Behavior

- nx list --json outputs all local and installed plugins with their
paths and capability types
- nx list <plugin> --json outputs detailed structured JSON including
resolved paths to each generator/executor implementation and schema
  - nx list <plugin> (text mode) now also shows the plugin's root path
2026-02-24 10:34:41 +01:00
Samuel Briole bdeeb036fb fix(bundling): skip unnecessary type-check in TS Solution Setup when skipTypeCheck is true (#34493)
## Current Behavior

In TS Solution Setup, the esbuild executor forces `runTypeCheck` even
when `skipTypeCheck: true` and `declaration: false`, due to the `||
options.isTsSolutionSetup` condition in `esbuild.impl.ts` (lines 139-140
and 195).

When `declaration: false`, this type check runs in `noEmit` mode with
`ignoreDiagnostics: true` — making it **completely pointless** (no
declarations emitted, no diagnostics reported). Its only observable
effect is writing a poisoned 19-byte tsbuildinfo file that causes race
conditions with `tsc --build`.

## Expected Behavior

When `skipTypeCheck: true` and `declaration: false`, the esbuild
executor should not run type checking at all. The `isTsSolutionSetup`
override should only force type checking when declarations actually need
to be generated.

## Fix

### Primary: Skip unnecessary type check (`esbuild.impl.ts`)

```diff
  // Non-watch mode (line 195)
- if (!options.skipTypeCheck || options.isTsSolutionSetup) {
+ if (!options.skipTypeCheck || (options.isTsSolutionSetup && options.declaration)) {

  // Watch mode (lines 139-140)
- options.isTsSolutionSetup
+ (options.isTsSolutionSetup && options.declaration)
```

Only force type checking in TS Solution Setup when declarations need to
be generated. This eliminates the pointless type check entirely.

### Defense-in-depth: Prevent tsbuildinfo in `noEmit` mode
(`run-type-check.ts`)

```diff
- : { noEmit: true };
+ : { noEmit: true, composite: false };
```

Setting `composite: false` alongside `noEmit: true` prevents TypeScript
from writing tsbuildinfo files, protecting against this class of bug
from any caller of `runTypeCheck`.

## Why This is Safe

| Scenario | Before | After |
|----------|--------|-------|
| `skipTypeCheck: false`, `declaration: false`, `isTsSolutionSetup:
true` | Runs type check (noEmit) | Still runs (`!false \|\| ...` = true)
|
| `skipTypeCheck: false`, `declaration: true`, `isTsSolutionSetup: true`
| Runs type check (emitDeclarationOnly) | Still runs |
| `skipTypeCheck: true`, `declaration: true`, `isTsSolutionSetup: true`
| normalize.ts overrides skipTypeCheck to false; runs type check | Still
runs (same normalization) |
| **`skipTypeCheck: true`, `declaration: false`, `isTsSolutionSetup:
true`** | **Runs pointless type check (noEmit + ignoreDiagnostics),
writes poisoned tsbuildinfo** | **Skipped entirely** |

The only behavior change is in the last row — the case where the type
check was doing nothing useful but causing harm.

## Related Issue(s)

Fixes #34492

---------

Co-authored-by: Jack Hsu <jack.hsu@gmail.com>
2026-02-23 22:32:01 -05:00
Jack Hsu 4b1bf5f7ba docs(node): add pruning guide for Docker deployments (#34560)
## Current Behavior

Users migrating to Nx 20's TS Solution Setup lose `generatePackageJson`
support and have no documentation on the replacement prune workflow
(`prune-lockfile`, `copy-workspace-modules`). The error message tells
them to "unset the option" but doesn't explain what to do instead.

## Expected Behavior

A dedicated guide at
`/docs/technologies/node/guides/deploying-node-projects` covers the full
prune workflow: when to use pruning vs bundling, target configuration,
Dockerfile setup, and step-by-step migration from `generatePackageJson`.

Also updated the existing bundling guide to match the same structure
(intro table, cross-links, style guide compliance). The two articles are
sister guides covering the two ways to deploy Node.js apps: bundle
everything into a single file, or prune dependencies for a
`node_modules`-based install.

Cross-links added from the bundling guide and ci-deployment guide.

## Related Issue(s)

Closes #30146

---------

Co-authored-by: nx-cloud[bot] <71083854+nx-cloud[bot]@users.noreply.github.com>
Co-authored-by: jaysoo <jaysoo@users.noreply.github.com>
2026-02-23 16:00:46 -05:00
Jack Hsu f935d9a7bf docs(release): document checkAllBranchesWhen type and behavior (#34515)
## Current Behavior
The `checkAllBranchesWhen` option is documented as type `string` with a
minimal description, which does not match the actual implementation.

## Expected Behavior
Document the correct type (`boolean | string[]`) and explain the default
branch resolution behavior, the three value modes (true, false,
string[]), and when this option is useful.

## Related Issue(s)
Closes DOC-414

---------

Co-authored-by: nx-cloud[bot] <71083854+nx-cloud[bot]@users.noreply.github.com>
Co-authored-by: jaysoo <jaysoo@users.noreply.github.com>
2026-02-23 15:47:41 -05:00
Jason Jean dd325d790a fix(core): retry entire SQLite transaction on DatabaseBusy (#34533)
## Current Behavior

When multiple Nx processes (task hasher, daemon, workers) access the
SQLite database concurrently, the `NxDbConnection::transaction()` method
only retries the BEGIN step using the `retry_db_operation_when_busy!`
macro. Operations executed inside the transaction and the COMMIT are not
retried, so if the database is busy during those steps, the task crashes
with:

```
Error: DB transaction operation error: SqliteFailure(Error { code: DatabaseBusy, extended_code: 5 }, Some("database is locked"))
```

This is particularly common during parallel task hashing with continuous
tasks, where `TaskDetails.recordTaskDetails()` and `RunningTasksService`
compete for write access.

## Expected Behavior

The entire transaction (begin, execute, commit) is retried as a single
unit when any step encounters a `DatabaseBusy` error. If the database is
busy during the operation or commit, the transaction is automatically
rolled back (via drop) and retried with the same exponential backoff
used everywhere else.

## Related Issue(s)

<!-- No public issue linked -->
2026-02-23 12:28:58 -05:00
Jason Jean ed786fb12c chore(repo): upgrade fast-xml-parser to 5.3.7 to fix CVE-2026-25896 (#34555)
## Current Behavior

The NPM audit CI job is failing due to a critical XSS vulnerability
(CVE-2026-25896) in `fast-xml-parser` version 4.5.3.

## Expected Behavior

The NPM audit should pass with no critical vulnerabilities.

## Related Issue(s)

Fixes the failing NPM audit CI run:
https://github.com/nrwl/nx/actions/runs/22288455713

---

This PR upgrades `fast-xml-parser` from `^4.2.7` to `^5.3.7` to address
GHSA-m7jm-9gc2-mpf2, a critical XSS vulnerability that allows entity
encoding bypass via regex injection in DOCTYPE entity names.

The package is only used in
`scripts/documentation/internal-link-checker.ts` for parsing XML
sitemaps, so the risk of this upgrade is low.
2026-02-23 12:22:36 -05:00
Jason Jean cc5eeefde9 feat(core): add preferBatch executor option (#34293)
## Current Behavior

Batch mode is binary:
- `--batch` flag → batch ALL executors that support it
- No flag → batch NOTHING

This means users of gradle/maven must always remember to pass `--batch`
to get the performance benefits.

## Expected Behavior

Plugin authors can now set `preferBatch: true` in their executor config
to indicate batch mode should be used by default. Users can still
opt-out with `--no-batch`.

Three states:
- `--batch` → batch everything
- `--no-batch` → batch nothing  
- (not specified) → use each executor's `preferBatch` preference

| `--batch` flag | `preferBatch` | Result |
|----------------|---------------|--------|
| `true`         | any           | Batch  |
| `false`        | any           | No batch |
| not set        | `true`        | Batch  |
| not set        | `false`/undefined | No batch |

## Changes

- Added `preferBatch?: boolean` to `ExecutorJsonEntryConfig` and
`ExecutorConfig` interfaces
- Updated `--batch` default from `false` to `undefined` to allow
`preferBatch` to decide
- Modified batch scheduling logic to respect `preferBatch`
- Enabled `preferBatch: true` for gradle and maven executors
- Added 5 unit tests covering all `preferBatch` scenarios

## Related Issue(s)

<!-- Link any related issues here -->
2026-02-23 12:19:15 -05:00
Jason Jean cf53d15ae5 chore(repo): update nx to 22.6.0-beta.2 (#34556)
Updating Nx from 22.6.0-beta.1 to 22.6.0-beta.2

---------

Co-authored-by: nx-cloud[bot] <71083854+nx-cloud[bot]@users.noreply.github.com>
2026-02-23 11:50:54 -05:00
Mathias Schopmans 7351e21150 fix(webpack): ensure safe process.env fallback replacement (#34464)
PR #30826 introduced a fallback definition for `process.env`:

```ts
{ 'process.env': '{}' }
```

Since `DefinePlugin` performs raw textual replacement, this can generate
invalid JavaScript when user code accesses environment variables via dot
notation:

```ts
process.env.SOME_KEY
```

becomes:

```js
{}.SOME_KEY
```

`{}` is parsed as a block statement (not an object literal), resulting
in:


> Unexpected token: punc (.)

This PR updates the fallback to a parenthesized object literal:

```ts
{ 'process.env': '({})' }
```

which produces valid output:

```js
({}).SOME_KEY
```

This preserves the intended bundle-size optimization while ensuring
syntactically correct output for standard `process.env.X` access
patterns.


## Related Issue(s)
Refs #30826
Fixes #34460 

//CC @Coly010 @coolassassin
2026-02-23 16:23:21 +00:00
MaxKless c5ab66152a feat(core): add AI agent mode to nx import (#34498)
## Current Behavior

`nx import` relies on interactive prompts (enquirer) and spinners (ora)
for user interaction. AI agents cannot parse this output or respond to
prompts, making `nx import` unusable in agent workflows.

## Expected Behavior

When `isAiAgent()` is true, `nx import` now:
- Skips all interactive prompts and spinners
- Emits NDJSON progress to stdout (`starting`, `cloning`, `filtering`,
`merging`, `detecting-plugins`, `complete`)
- Returns structured `needs_input` when required args are missing (all
at once to minimize round-trips)
- Returns structured `needs_input` for plugin selection when `--plugins`
flag is not provided
- Returns structured success/error results with hints and next steps
- Supports new `--plugins` flag (`skip`/`all`/comma-separated list)

Shared AI output types extracted from `init` into
`packages/nx/src/command-line/ai/ai-output.ts` for reuse across
commands.
2026-02-23 16:51:10 +01:00
MaxKless 1805301941 fix(misc): update maven & gradle icons to java duke icon (#34508)
duke is an official and open-source icon so we'll use it
<img width="1601" height="644" alt="image"
src="https://github.com/user-attachments/assets/3e95d94e-76d6-482a-ac09-bbe17a9f076a"
/>
<img width="1191" height="516" alt="image"
src="https://github.com/user-attachments/assets/151982fa-b497-4569-bf17-26f0d064d414"
/>

---------

Co-authored-by: Jason Jean <jasonjean1993@gmail.com>
Co-authored-by: nx-cloud[bot] <71083854+nx-cloud[bot]@users.noreply.github.com>
2026-02-23 09:33:39 -05:00
MaxKless e8633e0299 fix(core): preserve existing source properties in claude plugin config (#34499)
## Current Behavior

Running `configure-ai-agents` overwrites the entire `source` object in
`extraKnownMarketplaces['nx-claude-plugins']`, removing any user-added
properties like `ref`.

## Expected Behavior

User-added source properties (e.g. `ref`) are preserved, while `source`
and `repo` are always set to the correct values.
2026-02-23 21:45:51 +09:00
Colum Ferry 6bcaa46864 fix(angular): use SASS indented syntax in nx-welcome component when style is sass (#34510)
The nx-welcome component inline styles were always using CSS/SCSS syntax
(with braces and semicolons) regardless of the selected style option.
When --style=sass is chosen, the component now correctly uses SASS
indented syntax (no braces or semicolons) matching the expected
behavior for the .sass file format.

Fixes #33489
2026-02-23 11:50:43 +00:00
MaxKless d545472da8 feat(core): improve AX of configure-ai-agents with auto-detection (#34496)
## Current Behavior

When `configure-ai-agents` is invoked from within an AI agent (e.g.
Claude Code), it either shows an interactive multi-select prompt (which
the agent can't interact with) or requires `--agents` and
`--no-interactive` flags to work correctly. This makes the experience
awkward when AI agents call the command as part of workspace setup.

## Expected Behavior

When an AI agent is detected (via environment variables like
`CLAUDECODE`), the command now:

1. **Auto-configures the detected agent** if it's not yet configured,
partially configured, or outdated — no prompts needed
2. **Auto-updates any other outdated agents** alongside the detected one
3. **Reports non-configured agents** with a suggested `nx
configure-ai-agents --agents ...` command
4. **Reports up-to-date status** if the detected agent is already fully
configured

When `--agents` is explicitly passed, detection is ignored entirely
(existing behavior preserved). `--check` mode also works with detection
— it checks the detected agent plus all other configured agents.

Additionally:
- Strips AI agent detection env vars (`CLAUDECODE`, `CLAUDE_CODE`,
`OPENCODE`, `GEMINI_CLI`, etc.) from e2e subprocess environments to
prevent the test runner's environment from leaking into tests
- Fixes e2e tests to use `AGENTS.md` (not `GEMINI.md`) for gemini
assertions, matching what the gemini generator actually creates for
fresh installations

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: nx-cloud[bot] <71083854+nx-cloud[bot]@users.noreply.github.com>
2026-02-23 20:45:35 +09:00
Leosvel Pérez Espinosa 025db33a75 cleanup(repo): avoid unnecessary project graph recomputations (#34423)
## Current Behavior

- Cypress `start-dev-server.ts` file creates a port lock file next to
the source code
- Native temp DB files created by tests are not properly ignored
- Astro config timestamp file is not ignored

These all trigger watch file change events, which cause the project
graph to be recomputed unnecessarily.

## Expected Behavior

Output files should not trigger watch file change events. The project
graph should not be recomputed unnecessarily.
2026-02-23 10:08:05 +00:00
Leosvel Pérez Espinosa 731db47fd7 fix(misc): bump minimatch to 10.2.1 to address CVE-2026-26996 (#34509)
## Current Behavior

Several Nx packages directly depend on a minimatch version with a
high-severity vulnerability
(https://github.com/advisories/GHSA-3ppc-4f35-3m26).

## Expected Behavior

Several Nx packages should depend directly on a minimatch version that
does not include the reported high-severity vulnerability.

Note: unsafe `minimatch` versions can still be pulled in transitively.
Upstream deps need to be updated, and then we need to update the Nx
packages to newer versions.

## Related Issue(s)

Fixes #34507

---------

Co-authored-by: Jason Jean <jasonjean1993@gmail.com>
2026-02-23 09:42:07 +00:00
Copilot 4b6aea9f5e docs(core): clarify trailing slash requirement for inputs directory paths (#33664)
## Current Behavior

Users specifying directory paths in `inputs` without a trailing slash or
glob pattern find that files are not matched. For example,
`{projectRoot}/src` does not match any files, while `outputs` allows
naked directory paths without issue.

## Expected Behavior

Documentation clearly explains that directory paths in `inputs` require
a trailing slash or glob pattern:

```jsonc
{
  "inputs": [
    "{projectRoot}/src/",       // ✓ Works (trailing slash)
    "{projectRoot}/src/**/*",   // ✓ Works (glob pattern)
    "{projectRoot}/src"         // ✗ Does NOT work
  ]
}
```

### Changes

- **Reference doc** (`reference/inputs.mdoc`): Added "Directory Paths"
section explaining the requirement with examples
- **Guide** (`configure-inputs.mdoc`): Added callout warning at top
alerting users to this behavior
- Both docs note the difference from `outputs`, which do support naked
directory paths

## Related Issue(s)

Fixes
https://linear.app/nxdev/issue/NXC-2102/clarify-trailing-slash-requirement-for-inputs-in-directory-paths

Co-authored-by: Steven Nance <steven@nrwl.io>
2026-02-23 18:35:55 +09:00
Altan Stalker 0568059fb8 chore(repo): force nx-dev:prebuild-banner onto linux-extra-large (#34535)
Temp fix while scheduling is fixed for real

---------

Co-authored-by: FrozenPandaz <jasonjean1993@gmail.com>
2026-02-20 18:24:33 -05:00
Craigory Coppola 3e5df300ab feat(core): add commands for debugging cache inputs / outputs (#34414)
## Current Behavior
There's not a great way to troubleshoot or test inputs and outputs
configurations on tasks.

## Expected Behavior
Adds `nx show target` to enable users to debug inputs and outputs. It
has flags `--inputs`, `--check-input`, `--outputs`, and `--check-output`
to list or test specific file patterns.

<img width="1077" height="198" alt="image"
src="https://github.com/user-attachments/assets/7ffd4502-1542-40e6-867c-37f70440a421"
/>

<img width="1077" height="105" alt="image"
src="https://github.com/user-attachments/assets/eb5b09e1-23fb-4710-9fb0-84f0c8e80c14"
/>

<img width="1077" height="538" alt="image"
src="https://github.com/user-attachments/assets/74c60668-285c-4195-ba77-6693a66e4897"
/>

<img width="1077" height="318" alt="image"
src="https://github.com/user-attachments/assets/ee228c43-98c2-441d-8b9d-277b38213e4d"
/>

<img width="1077" height="92" alt="image"
src="https://github.com/user-attachments/assets/8cca6553-4362-4662-b948-723abcc75671"
/>

<img width="1077" height="74" alt="image"
src="https://github.com/user-attachments/assets/6dc2188e-5dc5-4b5f-afd4-2a492a896e1d"
/>

---

<img width="492" height="430" alt="image"
src="https://github.com/user-attachments/assets/ae22bb2e-92c2-4c75-ad6f-b9cccda3def4"
/>

---------

Co-authored-by: nx-cloud[bot] <71083854+nx-cloud[bot]@users.noreply.github.com>
Co-authored-by: FrozenPandaz <jasonjean1993@gmail.com>
2026-02-20 17:35:50 -05:00
Jack Hsu 221ed882fa fix(misc): prevent nxCloudId from being generated for new workspaces (#34532)
## Current Behavior

When creating a new workspace using `create-nx-workspace` with the
"custom" preset flow, an `nxCloudId` is generated and added to
`nx.json`. This happens even though the onboarding flow is supposed to
handle Cloud setup separately via a short URL.

## Expected Behavior

New workspaces created via `create-nx-workspace` should not have
`nxCloudId` set in `nx.json`. Instead, a short URL is provided for users
to finish Cloud onboarding on their own. The `nxCloud: 'skip'` option is
now passed for the custom flow to prevent the ID from being generated.

E2E tests are updated to verify that `nxCloudId` is undefined in the
generated `nx.json` across all workspace presets.

## Related Issue(s)

N/A - internal fix for workspace creation behavior.

---------

Co-authored-by: nx-cloud[bot] <71083854+nx-cloud[bot]@users.noreply.github.com>
2026-02-20 19:56:58 +00:00
Craigory Coppola df8a2c43f4 fix(core): commands shouldn't hang when passing --help (#34506)
## Current Behavior
`--help` on commands that hit yargs help are hanging

## Expected Behavior
It doesn't hang. This contains a quick fix in adding the process.exit
call, but also adds the unref needed to maintain previous working
behavior. We'll need to investigate long term if additional areas keep
commands alive, but adding this unref theoretically allows removing the
process.exit calls from `nx show`

## Related Issue(s)
<!-- Please link the issue being fixed so it gets closed when this is
merged. -->

Fixes #

---------

Co-authored-by: Jason Jean <jasonjean1993@gmail.com>
2026-02-20 13:49:21 -05:00
Jason Jean 391d23c65e chore(repo): re-enable e2e tests disabled by api-extractor issue (#34519)
## Current Behavior

14 e2e test suites were disabled (`xdescribe`) due to an ESM import
issue in `@microsoft/api-extractor@7.57.0` (see
https://github.com/qmhc/unplugin-dts/issues/461).

## Expected Behavior

With the upstream issue resolved, all 14 e2e test suites are re-enabled
(`describe`) and should pass normally.

## Related Issue(s)

Reverts #34516
2026-02-20 12:17:20 -05:00
Jason Jean 092cea6073 chore(repo): update nx to 22.6.0-beta.1 (#34527)
Updating Nx from 22.5.0-beta.5 to 22.6.0-beta.1
2026-02-20 11:21:34 -05:00
Jack Hsu de2dc7ca13 fix(nextjs): reset daemon client after project graph creation in withNx (#34518)
## Current Behavior

Running `nx test` for Next.js projects causes Jest to hang with:
```
Jest did not exit one second after the test run has completed.
```

This happens because `next/jest` loads `next.config.js`, which calls
`withNx` → `createProjectGraphAsync()`. The daemon client socket
connection is left open, keeping the Node.js event loop alive and
preventing Jest from exiting. Non-Next.js projects are unaffected since
they don't trigger this code path.

## Expected Behavior

Jest exits cleanly after tests complete for Next.js projects, without
needing `forceExit: true`.

## Fix

Pass `resetDaemonClient: true` to `createProjectGraphAsync()` in
`packages/next/plugins/with-nx.ts`. This tells the project graph
function to call `daemonClient.reset()` after fetching the graph, which
closes the socket and allows Jest to exit.

### Verification

| Scenario | Before | After |
|----------|--------|-------|
| `nx test next-app` | Hangs | Exits cleanly |
| `NX_DAEMON=false nx test next-app` | Exits cleanly | Exits cleanly |
| Direct `npx jest` | Exits cleanly | Exits cleanly |
| Non-Next.js `nx test react-lib` | Exits cleanly | Exits cleanly |

## Related Issue(s)

Fixes #32880
2026-02-20 11:08:50 -05:00
Jason Jean 5236e02308 chore(maven): bump maven plugin version to 0.0.14 (#34505)
## Current Behavior

The Maven plugin is on version `0.0.13`.

## Expected Behavior

The Maven plugin is bumped to version `0.0.14`, with a migration
generated for Nx `22.6.0-beta.1`.
2026-02-19 18:27:14 -05:00
Altan Stalker 8e1d873edc chore(core): enable nx cloud verbose logging (#34524)
## Current Behavior
Agents are silent and hard to diagnose

## Expected Behavior
Agents should print debug logs without making all of Nx print debug logs
2026-02-19 22:41:35 +00:00
Jason Jean 4ca3ee97c3 chore(repo): disable e2e tests broken by @microsoft/api-extractor@7.57.0 (#34516)
## Current Behavior

14 e2e tests are failing across master with "Failed to process project
graph" errors. The root cause is `@microsoft/api-extractor@7.57.0` which
has a broken ESM export (`ConsoleMessageId`). When `@nx/vite/plugin` or
`@nx/vitest` plugins load `vite.config.mts` files, they transitively
import api-extractor which crashes.

## Expected Behavior

Broken e2e tests are disabled via `xdescribe` so they no longer block
CI. Tests should be re-enabled once the upstream api-extractor ESM issue
is fixed.

## Disabled Tests

| Project | Test File |
|---------|-----------|
| e2e-vite | `vite.test.ts`, `vite-legacy.test.ts`,
`vite-ts-solution.test.ts` |
| e2e-vue | `vue.test.ts`, `vue-legacy.test.ts`,
`vue-ts-solution.test.ts` |
| e2e-js | `js-ts-solution.test.ts` |
| e2e-web | `web-vite.test.ts` |
| e2e-react | `react-vite.test.ts`, `react-ts-solution.test.ts` |
| e2e-next | `next-ts-solutions.test.ts` |
| e2e-release | `release-publishable-libraries.test.ts`,
`release-publishable-libraries-ts-solution.test.ts` |
| e2e-storybook | `storybook-nested.test.ts` |

## Related Issue(s)

Upstream: https://github.com/qmhc/unplugin-dts/issues/461
2026-02-19 19:16:57 +00:00
Jason Jean e6ad74afed chore(maven): upgrade maven-shade-plugin to 3.6.0 (#34514)
## Current Behavior

The `maven-shade-plugin` at version 3.5.0 intermittently fails on CI
with:

```
Could not replace original artifact with shaded artifact!
```

This is a file-locking race condition where the plugin fails to
atomically replace the original JAR with the shaded JAR.

## Expected Behavior

Upgrading to 3.6.0 resolves the intermittent CI failures by using
improved file-handling logic with better retry behavior during the
artifact replacement step.

## Related Issue(s)

N/A - fixes intermittent CI flakiness in `maven-batch-runner` builds.
2026-02-19 10:28:14 -08:00
Ondrej Kelle 79f41e54af feat(core): use static_vcruntime to avoid msvcrt dependency (#19781)
Closes #19779

<!-- Please make sure you have read the submission guidelines before
posting an PR -->
<!--
https://github.com/nrwl/nx/blob/master/CONTRIBUTING.md#-submitting-a-pr
-->

<!-- Please make sure that your commit message follows our format -->
<!-- Example: `fix(nx): must begin with lowercase` -->

## Current Behavior
When targeting Windows the resulting binary (nx.dll) dynamically links
against Microsoft Visual C++ runtime (msvcrt140.dll). This means nx
won't be able to run on Windows systems without this runtime installed.

## Expected Behavior
I'd like to avoid this dependency by linking the runtime statically into
the nx binary. (This is also how e.g. cargo.exe for Windows is built.)

## Related Issue(s)

Fixes #19779

---------

Co-authored-by: Craigory Coppola <craigorycoppola@gmail.com>
2026-02-19 09:07:17 -05:00
Leosvel Pérez Espinosa 4f9be499b4 fix(core): reduce terminal output duplication and allocations in task runner (#34427)
## Current Behavior

Terminal output in the task runner is accumulated via repeated string
concatenation (`terminalOutput += chunk`). Each `+=` on a growing string
causes V8 to allocate a new, larger string and copy the old contents,
resulting in O(n²) allocation behavior for tasks with large output.
Additionally, `PseudoTtyProcess.onExit` didn't pass `terminalOutput` to
its callbacks, forcing callers like `TaskOrchestrator` to duplicate
output accumulation logic with a separate `onOutput` listener.

## Expected Behavior

- Terminal output is collected in `string[]` arrays and joined once at
the end, reducing intermediate allocations from O(n²) to O(n)
- `PseudoTtyProcess.onExit` now passes `terminalOutput` as a second
argument, matching the signature of other `RunningTask` implementations
- `TaskOrchestrator` no longer needs a special code path for
`PseudoTtyProcess` — unified `onExit` handling for all task types
- `tui-summary-life-cycle` accumulates output in chunks during execution
and stores the finalized string on task completion, allowing chunk
arrays to be GC'd
- `SeriallyRunningTasks` and `RunningNodeProcess` similarly switched to
chunk-based accumulation
- `BatchProcess` and `NodeChildProcessWithNonDirectOutput` lazily join
and cache their terminal output
2026-02-18 19:05:41 -05:00
Caleb Ukle 42b534366d docs(nx-dev): tech intro page structure improvements (#34450)
Work on making a tech intro pages more consistent with each other and
focus on "answering the 80%" for the given technology.

Focusing on 
- Angular
- Maven/Gradle
- react
- TS
- Vite
- Vitest/Jest

The changes are based around answering the following, where each
"category" of page might have a different set of depth for the answer.

1. Why do I want to use this plugin?
- Plugins are considered fully optional and are aimed at providing
better DX for a technology, such as inferred setup, generators,
migrations.
- some plugins (like TSC) might have special call outs in some of this,
but generally the same for all plugins.
2. How do I use this plugin in my workspace?
  - also pretty commonly the "same" for all plugins in terms of "setup"
- where they differ is mostly for frameworks, e.g. Angular, React.
You're looking at setting up a project to use these tools
- For Build/Test tools you're looking at adding to an existing project,
or converting from one to another.
- Build/Test tools are "means to an end", so should callout if the goal
is tool + framework in a "new" context point to the framework based
plugin page. Otherwise, show adding to an existing project like React
project.
3. What do I need to know about using this plugin?
- understanding finer details of a plugin options, e.g. buildable &
publishable
  - extra generators for the plugin. e.g. "convert-to-swc"
- generally I like the idea of having a "CI considerations" where we
talk about CI setups that can help, e.g. options or batch mode etc.


closes DOC-407

---------

Co-authored-by: nx-cloud[bot] <71083854+nx-cloud[bot]@users.noreply.github.com>
Co-authored-by: barbados-clemens <barbados-clemens@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: graphite-app[bot] <96075541+graphite-app[bot]@users.noreply.github.com>
2026-02-18 22:25:23 +00:00
Caleb Ukle 7528cc51fa fix(nx-dev): update breadcrumb links to match sidebar (#34500)
navigation of the breadcrumbs could lead to confusing state since they
were based around the folder structure.

Breadcrumbs are now based around the sidebar structure so they match the
hierarchy of content.

Note: I left the existing index file based route pages in place in case
there are any links people have booked marked/linked to in other
locations. these will get cleaned up when we finally rewrite all the
URLs to their new content locations
2026-02-18 22:23:00 +00:00
Caleb Ukle bbb1baa631 fix(nx-dev): widen search dialog (#34504) 2026-02-18 22:02:18 +00:00
Leosvel Pérez Espinosa 91b350efa8 fix(core): skip stale recomputations and prevent lost file changes in daemon (#34424)
## Current Behavior

When file changes arrive rapidly, the daemon triggers multiple
concurrent project graph recomputations that all run to completion —
wasting CPU/memory on redundant work and returning stale results.

Additionally, after processing file changes, the daemon clears all
tracked files indiscriminately. Files that changed mid-recomputation are
silently lost and never reflected in the project graph until another
unrelated file change arrives.

## Expected Behavior

Stale recomputations detect when a newer one has started and exit early,
chaining to the newer promise so callers always get the freshest result.

File change tracking now uses versioned maps. Each batch of file watcher
events gets a unique version, and only files matching the snapshotted
version are cleared after processing. Files that changed
mid-recomputation are preserved and picked up by the next cycle.
2026-02-18 16:18:43 -05:00
Jason Jean 50ca951540 fix(repo): fix e2e CI failures from Node 22.12 incompatibility (#34501)
## Current Behavior

Two categories of e2e CI failures were observed in run
https://github.com/nrwl/nx/actions/runs/22127884259:

1. **`e2e-nx-init` and `e2e-js` fail on Node 22.12.0** with:
   ```
error eslint-visitor-keys@5.0.0: The engine "node" is incompatible with
this module.
   Expected version "^20.19.0 || ^22.13.0 || >=24". Got "22.12.0"
   ```
Node 22.12.0 is one minor version short of the `^22.13.0` range required
by `eslint-visitor-keys@5.0.0`.

2. **`e2e-nx` tests fail because `[isolated-plugin]` / `[plugin-worker]`
verbose messages leak into captured stdout**, causing:
- `JSON.parse(runCLI('show project --json'))` to throw `SyntaxError:
Unexpected token 'i', "[isolated-p"...`
- `expect(runCLI('show projects')).toEqual('')` to fail with worker
spawn noise
- The `@nx/workspace:infer-targets` test to unexpectedly find
`@nx/remix` in output (from a worker spawn message)

Root cause: in `isolated-plugin.ts`, the plugin worker's stdout was
piped directly to `process.stdout`, so `[plugin-worker]` verbose
messages written by the worker ended up in the stdout captured by
`runCLI` in e2e tests.

## Expected Behavior

1. The CI matrix uses a Node 22.x version that satisfies `^22.13.0`.

2. Plugin worker verbose/diagnostic messages go to `process.stderr` (not
`process.stdout`), so they don't contaminate output captured by `runCLI`
in e2e tests. Both worker stdout and stderr now pipe to
`process.stderr`, and the max listener bump is consolidated to `+2` on
stderr.

## Related Issue(s)

N/A — identified from CI run
https://github.com/nrwl/nx/actions/runs/22127884259
2026-02-18 15:48:03 -05:00
MaxKless 18bfb0bc4a fix(maven): write output after each task in batch mode to ensure correct files are cached (#34400)
## Current Behavior
When running in maven 4 batch mode, the build state is recorded only
after the full batch is done.
This means that nx caching records the state of a task before build
state is recorded to disk.
When running another maven task that depends on this partially recorded
cache, the build state file is missing and we get errors.

## Expected Behavior
build state should be recorded after every task is done and before nx
caching can kick in. This way we can ensure that nx cache is correct.

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 11:22:18 -05:00
Eric Baer 3b0fb81570 feat(devkit): add NX_SKIP_FORMAT environment variable to skip Prettier formatting (#34336)
## Current Behavior

When running generators or migrations, Nx automatically skips Prettier
formatting if no root Prettier config is detected (added in #30426).
However, there's no way to explicitly skip Prettier formatting when a
config IS present but the user wants to bypass it for specific
operations.

This can be needed when:
- When running something like Oxfmt that may treat prettier slightly
differently, even with the same config (this is the main thing I ran
into)
- Running migrations where Prettier reformatting causes unintended side
effects (e.g., breaking `eslint-disable` comments)
- Temporarily disabling formatting for debugging purposes
- Using a formatter that coexists with Prettier in the workspace but
should take precedence for certain files

## Expected Behavior

Users can set `NX_SKIP_FORMAT=true` to explicitly skip Prettier
formatting in generators and migrations, regardless of whether Prettier
is configured. TSConfig path sorting (controlled by
`sortRootTsconfigPaths` or `NX_FORMAT_SORT_TSCONFIG_PATHS`) continues to
work independently.

```bash
NX_SKIP_FORMAT=true nx migrate --run-migrations
NX_SKIP_FORMAT=true nx g @nx/react:app my-app
```

## Related Issue(s)

Related to #30403 and #30426. This enhancement adds explicit user
control for cases where auto-detection of Prettier configuration isn't
sufficient.
2026-02-18 09:56:43 -05:00
Craigory Coppola bdbc14902e feat(core): add --otp to top-level nx release command and detect EOTP errors (#34473)
## Current Behavior
When publish fails due to missing OTP code, its not clear as a user who
is using the top level command what to do next.

## Expected Behavior
Add the --otp flag to the top-level `nx release` command so users can
provide a one-time password for 2FA-enabled registries when running the
full release orchestration (version + changelog + publish).

When publish fails due to an expired or missing OTP (EOTP error),
display a helpful warning listing affected projects and the exact
command to re-run the publish step in isolation with a new OTP.

## Related Issue(s)
<!-- Please link the issue being fixed so it gets closed when this is
merged. -->

Fixes #
2026-02-18 08:05:40 -05:00
MaxKless dc6716828b feat(core): improve codex support for configure-ai-agents (#34488)
Codex has only basic MCP/AGENTS.md support right now. Also, because it
used to have only global-level config files, we had some extra logic
around configuring that.

We want Codex to get the latest skills too (they don't support custom
subagents yet, though) and use project-level config files that they now
support.
2026-02-18 20:14:27 +09:00
MaxKless 8de74d0984 feat(core): implement configure-ai-agents outdated message after tasks (#34463)
After running nx build (or any task), the daemon now shows a hint if
your AI agent configuration is outdated: "Your AI agent configuration is
outdated. Run nx configure-ai-agents to update."

The daemon computes and caches the full agent configuration status
(fully configured, outdated, partially configured, non-configured) using
latest Nx from npm, so the check is always against the newest available
configuration. Running nx configure-ai-agents resets the daemon's cache
so the message disappears on the next build.

  Key changes

- Daemon agent status endpoint: New GET_CONFIGURE_AI_AGENTS_STATUS /
RESET_CONFIGURE_AI_AGENTS_STATUS message types. The handler fires off
computation in the background and returns immediately (never blocks the
request). Results are cached for the daemon's lifetime.
- Shared latest-nx module: Extracted the "install nx@latest to tmp"
logic from nx-console-operations into daemon/server/latest-nx.ts so both
Nx Console and AI agents handlers share a single cached installation.
Includes a race-condition guard (in-flight promise deduplication).
- Post-task outdated hint: run-command.ts queries the daemon after task
execution and prints a single dim line if agents are outdated.
- Daemon reset from configure-ai-agents: The CLI sets NX_DAEMON=false
for configure-ai-agents, so we bypass daemonClient.enabled() and use
isServerAvailable() directly to reach an already-running daemon. The
socket is closed in a finally block so the process exits cleanly.
- Async editor detection (Rust): Made isEditorInstalled,
canInstallNxConsoleForEditor, installNxConsole, and related napi
functions async so they run on the libuv thread pool instead of blocking
Node's event loop. This prevents the daemon from stalling for ~3.5s when
checking editor extensions.
- output.logRawLine: New helper that prints a single line without the NX
prefix.

---------

Co-authored-by: nx-cloud[bot] <71083854+nx-cloud[bot]@users.noreply.github.com>
2026-02-18 18:53:41 +09:00
MaxKless b89c3084e7 feat(core): automatically set up ai agents in cnw/init when run from within an ai agent (#34469)
We want to minimize prompts for people and agents. But we also want to
help them by setting up nx config for them so their agents can work
optimally.
If they're executing `nx init` or `create-nx-workspace` from within an
agent, it's a reasonable assumption that they'll want the best AI config
for that specific agent - so we set it up for them.
2026-02-18 18:10:13 +09:00
Simon Heather bdf6d257b7 docs(core): add cacheKeyPrefix option to s3 remote cache options (#34157)
This pull request updates the documentation for the S3 cache plugin to
add the missing `cacheKeyPrefix` setting.

Text taken from https://github.com/nrwl/nx/pull/31395

Fixes #34147

---------

Co-authored-by: Caleb Ukle <caleb@nrwl.io>
Co-authored-by: Caleb Ukle <caleb.ukle+github@pm.me>
Co-authored-by: Simon Heather <simon.heather@yulife.com>
2026-02-17 22:24:35 -05:00
Leosvel Pérez Espinosa 678cd321f5 fix(core): replace buggy ignore-files trie with correct path-component gitignore matching (#34447)
## Current Behavior

The `nx watch` file watcher uses the `ignore-files` and
`watchexec-filterer-ignore` crates to handle `.gitignore` matching.
These crates use a trie-based approach that has a bug with
path-component matching — certain gitignore patterns (e.g., prefix
patterns) don't match correctly, causing files that should be ignored to
trigger unnecessary watch events.

## Expected Behavior

Gitignore patterns are matched correctly using per-directory `Gitignore`
instances from the `ignore` crate — the same crate already used by the
file walker. Each `.gitignore` file is scoped to its directory, and
matching is done deepest-first so that nested gitignores take priority —
matching standard git behavior.

### What changed

- Replaced `ignore-files` + `watchexec-filterer-ignore` with direct use
of `ignore::gitignore::{Gitignore, GitignoreBuilder}`, aligning the
watcher with the approach already used by the file walker
- Each `.gitignore` is now compiled as a standalone instance tied to its
parent directory
- Gitignore evaluation walks deepest-first; first match wins
- `.nxignore` matching now uses `matched_path_or_any_parents` for
correct ancestor checking
- `create_filter` is now synchronous (no longer `async`) since the new
approach doesn't need async I/O
- Removed 2 crate dependencies (`ignore-files`,
`watchexec-filterer-ignore`)
2026-02-17 18:30:22 -05:00
Jay Bell a0e34557f9 fix(core): use workspace root for path resolution when baseUrl is not set (#34453)
## Current Behavior
                                                            
When a project-level `tsconfig.json` (e.g., `apps/aurora/tsconfig.json`)
inherits `paths` via `extends` from `tsconfig.base.json` at the
workspace root and no explicit `baseUrl` is set, Nx incorrectly resolves
`./`-prefixed path mappings relative to the project tsconfig directory
instead of the workspace root where the paths were defined.
This causes errors when loading TypeScript config files (e.g.,
`rspack.config.ts`) that import workspace libraries using path aliases:
  NX   Cannot find module './libs/plugins/rspack/src'

`@swc-node/register`'s `readDefaultTsConfig` auto-sets `baseUrl` to
`dirname(tsConfigPath)` (the project directory) when not explicitly
configured, causing SWC to rewrite imports to incorrect relative paths
during transpilation.

  ## Expected Behavior

Path aliases defined in `tsconfig.base.json` (e.g.,
`"@trellis/plugins/rspack": ["./libs/plugins/rspack/src/index.ts"]`)
should resolve relative to the workspace root when no `baseUrl` is
configured.

This is needed so that when using `tsgo` and needing to prefix all paths
with `./` (no more `baseUrl` allowed) the paths are still resolved from
the right spot.

I tested this fix against our codebase on the branch I was trying to
switch to tsgo on and it seemed to work.

  ## Related Issue(s)

Fixes
https://discord.com/channels/1143497901675401286/1471627045694865581
2026-02-17 18:22:07 -05:00
Altan Stalker 5c7c9dd5fa chore(core): enable continuous assignment (#34471)
## Current Behavior
Continuous assignment is not enabled

## Expected Behavior
Continuous assignment is enabled
2026-02-17 18:17:15 -05:00
Juri Strumpflohner 4f31277f4f docs(repo): update CONTRIBUTING.md with Discord link (#34461)
## Current Behavior

CONTRIBUTING.md contains an outdated "How to Get Started Video" section
and references Stack Overflow for general questions.

## Expected Behavior

Remove outdated video section and point users to the Discord community
instead of Stack Overflow for general questions.

## Related Issue(s)

N/A
2026-02-17 18:15:38 -05:00
Colum Ferry c16377af25 feat(misc): use caret range for swc dependencies in pnpm catalog (#34487)
Use a range for the swc dependencies

Fixes #34472
2026-02-17 18:14:17 -05:00
Craigory Coppola 130cec466f fix(core): avoid blocking event loop during TUI PTY resize (#34385)
When switching from inline mode to full-screen TUI (or during window
resize), the PTY resize operation reparsed ALL raw terminal output
through a new vt100 parser synchronously on the event loop. For tasks
with large output, this caused a noticeable hang.

Add `resize_async()` which moves the expensive reparse to a background
thread using a snapshot-and-replay pattern:
1. Quick snapshot of raw output (brief read lock)
2. Expensive reparse on background thread (no locks held)
3. Quick swap with replay of any new output (brief write lock)

A generation counter prevents stale resizes from overwriting newer ones.

Also combine two separate O(n) scrollback processing calls in inline
mode into a single pass.
2026-02-17 18:07:43 -05:00
Copilot 65b94a1293 chore(repo): update copyright year to 2026 and refresh README description (#34437)
## Current Behavior

Copyright year shows 2017-2025 and README uses older tagline.

## Expected Behavior

Copyright reflects current year 2026 and README uses updated repository
description.

## Changes

- **LICENSE**: Updated copyright year from `2017-2025` to `2017-2026`
- **README.md**: Replaced heading and description
- New heading: "The Monorepo Platform that amplifies both developers and
AI agents. Nx optimizes your builds, scales your CI, and fixes failed
PRs automatically. Ship in half the time."
  - Removed redundant description line below heading

<!-- START COPILOT ORIGINAL PROMPT -->



<details>

<summary>Original prompt</summary>

> ## Update License and README
> 
> Please make the following changes:
> 
> 1. **Update LICENSE file**: Change the copyright year from `2017-2025`
to `2017-2026`
>    - File: `LICENSE`
> - Line 3: Update `Copyright (c) 2017-2025 Narwhal Technologies Inc.`
to `Copyright (c) 2017-2026 Narwhal Technologies Inc.`
> 
> 2. **Update README.md description**: Replace the current description
with the repository's official description
>    - File: `README.md`
> - Line 22: Change the heading from `# Smart Monorepos · Fast Builds`
to `# The Monorepo Platform that amplifies both developers and AI
agents. Nx optimizes your builds, scales your CI, and fixes failed PRs
automatically. Ship in half the time.`
> - Line 24: Remove the current description line: `Get to green PRs in
half the time. Nx optimizes your builds, scales your CI, and fixes
failed PRs. Built for developers and AI agents.`
> 
> The new README should have the repository description as the main
heading, followed immediately by the "Create a new Nx workspace with"
section.


</details>



<!-- START COPILOT CODING AGENT SUFFIX -->

*This pull request was created from Copilot chat.*
>

<!-- START COPILOT CODING AGENT TIPS -->
---

💬 We'd love your input! Share your thoughts on Copilot coding agent in
our [2 minute survey](https://gh.io/copilot-coding-agent-survey).

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: FrozenPandaz <8104246+FrozenPandaz@users.noreply.github.com>
Co-authored-by: Jason Jean <jasonjean1993@gmail.com>
2026-02-17 16:45:38 -05:00
Leosvel Pérez Espinosa 896a3f31ad fix(core): gate tui-logger init behind NX_TUI env var (#34426)
## Current Behavior

`tui_logger::init_logger()` and `TuiTracingSubscriberLayer` are
initialized unconditionally in `initialize_logger()`. This spawns a
background thread, causing unnecessary allocation churn and increased
processing.

## Expected Behavior

`tui_logger` is only initialized when `NX_TUI=true`, avoiding the
background thread and allocation overhead for all non-TUI contexts.
2026-02-17 16:40:48 -05:00
Caleb Ukle ea81b52442 chore(nx-dev): condense redirect rules (#34452)
## Current Behavior

We have ~1,657 redirect rules across `redirect-rules.js` and
`redirect-rules-docs-to-astro.js`, getting close to Netlify's limit and
we need room for more as the Astro migration continues.

## Expected Behavior

Reduced to **1,139 rules** (~31% reduction) by:

- Resolving duplicate/conflicting source paths across sections
- Flattening multi-hop redirect chains to point directly to final
destinations
- Consolidating groups of individual rules into wildcard patterns
(tutorials, CLI, helm, concepts, recipes, etc.)
- Removing old 2022-era sections (`schemaUrls`, `overviewUrls`,
`packagesIndexes`, `packagesDocuments`) whose destinations chain 3-5
hops deep and are long superseded by newer redirects
- made sure old links in nx code base still have redirects (will update
in future PR)

Build, tests, and internal link check all pass with no issues.

## Related Issue(s)

Fixes DOC-403

---------

Co-authored-by: nx-cloud[bot] <71083854+nx-cloud[bot]@users.noreply.github.com>
Co-authored-by: barbados-clemens <barbados-clemens@users.noreply.github.com>
2026-02-17 11:21:33 -06:00
Caleb Ukle edc9cc5af8 fix(nx-dev): use shared preview url for netlify deploy (#34467)
nextjs and astro should route to same preview deployments now


![wm_2026-02-16T19-40-04](https://github.com/user-attachments/assets/593c011a-ff78-4412-9bf0-ad186157f5d4)
2026-02-17 11:21:24 -06:00
Jack Hsu 2fa93afe2b feat(core): add agentic mode to nx init (#34418)
## Current Behavior

When AI agents (Claude Code, Cursor, Windsurf, etc.) run `nx init`, the
command works but:
- Uses interactive prompts that AI agents can't handle
- Outputs human-readable text that AI agents must parse
- Doesn't provide structured progress or error information

## Expected Behavior

When `nx init` detects an AI agent (via environment variables like
`CLAUDE_CODE=1`), it should:
- Skip interactive prompts and use sensible defaults
- Output structured NDJSON for progress updates, success, and errors
- Include detailed context for AI agents to understand and fix issues

## Changes

This PR adds agentic mode to `nx init`:

### `nx init` Changes
- Detect AI agents via `isAiAgent()` native function
- Auto-defaults: `interactive=false`, `nxCloud=false`, auto-detect `.nx`
installation
- NDJSON output with `type: progress|success|error`
- Error logs written to `.nx/ai-errors/` with full context
- Cursor restoration escape sequence skipped for AI agents (prevents
NDJSON corruption)

### Output Format
```jsonl
{"type":"progress","step":"starting","message":"Initializing Nx..."}
{"type":"success","nxVersion":"22.5.0","projectsDetected":1,"pluginsInstalled":["@nx/vite"]}
```

Or on error:
```jsonl
{"type":"error","message":"Failed to install","code":"INSTALL_ERROR","errorLogPath":".nx/ai-errors/nx-init-error-2025-01-15T10-30-00.log"}
```
2026-02-18 01:54:04 +09:00
MaxKless ee22084d1a fix(core): only pull configure-ai-agents from latest if local version is not latest (#34484)
## Current Behavior
we pull from latest all the time even if the current version is already
latest

## Expected Behavior
we can skip this extra work sometimes
2026-02-17 16:30:34 +00:00
Jack Hsu ca2fc0fa85 fix(misc): rewrite Framer URLs to nx.dev in HTML responses (#34445)
## Current Behavior

Pages proxied from Framer contain canonical URLs pointing to the Framer
domain (`ready-knowledge-238309.framer.app`), causing duplicate indexing
issues in search engines.

## Expected Behavior

Canonical URLs and other references in Framer-proxied pages now point to
`nx.dev`, ensuring proper SEO indexing.

### Implementation

Consolidated all Framer logic into a single Netlify edge function
(`rewrite-framer-urls.ts`) that:

1. Checks if the request path matches a Framer-proxied path (using
`FRAMER_REWRITES` env var)
2. Fetches directly from Framer (using `FRAMER_URL` env var)
3. Rewrites all Framer URLs to `nx.dev` in the HTML response (handles
`<link rel="canonical">`, `og:url`, etc.)
4. For non-Framer paths, passes through to Next.js

The edge function uses the `accept: ['text/html']` config to only run on
HTML requests, matching the pattern from `track-page-requests.ts` in
astro-docs.

The Next.js middleware has been removed since all Framer routing is now
handled by the edge function.

### Environment Variables

The edge function expects these env vars in Netlify (already added
previously):
- `NEXT_PUBLIC_FRAMER_URL`: e.g.,
`https://ready-knowledge-238309.framer.app`
- `NEXT_PUBLIC_FRAMER_REWRITES`: comma-separated list of paths, e.g.,
`/pricing,/enterprise`

## Demo

1. Go to https://deploy-preview-34445--nx-dev.netlify.app/
2. View source and look for canonical
3. See it is nx.dev not framer domain

<img width="1347" height="161" alt="image"
src="https://github.com/user-attachments/assets/d4a515da-e39f-41cb-a7b4-668fe0bedbbd"
/>


## Related Issue(s)

Closes CLOUD-4148
2026-02-17 10:53:46 -05:00
Steven Nance 0c14bcbe55 fix(release): remove unnecessary number from release return type (#34481)
## Current Behavior

The `release` function returned by `createAPI` has a return type of
`Promise<NxReleaseVersionResult | number>`. The `| number` union member
is inaccurate since the function always returns
`NxReleaseVersionResult`, which can mislead consumers of the
programmatic API.

## Expected Behavior

The return type is narrowed to `Promise<NxReleaseVersionResult>`,
accurately reflecting what the function actually returns and giving API
consumers correct type information.

Co-authored-by: Andreas Hörnicke <andreas.hoernicke@contentful.com>
2026-02-17 15:10:11 +00:00
MaxKless 08d899a2d2 docs(misc): update nx-mcp reference and tweak ai docs for skills (#34468)
we changed the default options of the nx mcp so we need to update docs
to reflect it
2026-02-17 22:23:39 +09:00
MaxKless 0d4160e968 docs(nx-dev): add MCP to skills blog post (#34428)
Blog post draft about the evolution from MCP tools to agent skills.

---------

Co-authored-by: nx-cloud[bot] <71083854+nx-cloud[bot]@users.noreply.github.com>
Co-authored-by: MaxKless <MaxKless@users.noreply.github.com>
Co-authored-by: Juri Strumpflohner <juri.strumpflohner@gmail.com>
Co-authored-by: graphite-app[bot] <96075541+graphite-app[bot]@users.noreply.github.com>
2026-02-17 09:28:34 +00:00
Craigory Coppola 8c15428f43 feat(core): support dependency filesets with ^{projectRoot} syntax (#34310)
## Current Behavior
`^` and `dependencies: true` only work for fileset inputs

## Expected Behavior
Adds support for inputs of the form `^{projectRoot}/**/*.ts` as
syntactic sugar for specifying a fileset input that should be collected
from dependency projects.

Previously, only named inputs could use the `^` prefix to include
dependencies (e.g., `^production`). Now filesets can also use this
syntax directly without needing to define a named input first.

Examples:
- `^{projectRoot}/**/*.ts` - include .ts files from all dependencies
- `^{workspaceRoot}/tools/**/*` - include workspace tools from
dependencies
- `{ fileset: '{projectRoot}/**/*.ts', dependencies: true }` - object
form

Detection is deterministic: if the string after `^` starts with
`{projectRoot}` or `{workspaceRoot}`, it's treated as a dependency
fileset; otherwise, it's treated as a named input reference.

## Related Issue(s)
<!-- Please link the issue being fixed so it gets closed when this is
merged. -->

Fixes #
2026-02-16 21:25:55 -05:00
Leosvel Pérez Espinosa 59b12edcb6 fix(core): prevent staggered and duplicate lines in dynamic output (#34462)
## Current Behavior

- In some `nx run-many` executions, terminal output can appear staggered
or visually misaligned instead of updating cleanly in place.
- For `run-many` cases that end up running a single task (especially
when TUI is not active), the spinner/status line can be rendered twice.

## Expected Behavior

- Dynamic terminal output updates should remain stable and aligned, with
clean in-place refreshes.
- Single-task `run-many` should display a single spinner/status line
with no duplicate rendering.
2026-02-16 17:31:07 +00:00
Juri dd3b79ebf4 fix(core): handle Ctrl+C gracefully in configure-ai-agents
Add uncaughtException handler for ERR_USE_AFTER_CLOSE to prevent
ugly stack trace when pressing Ctrl+C during enquirer prompts
(Node 24 stricter readline behavior). Matches existing pattern
used in nx init and create-nx-workspace.
2026-02-16 12:57:17 +01:00
Jason Jean d64e41dd99 fix(repo): revert sudo for global npm install in publish workflow (#34451)
## Current Behavior

The publish workflow uses `sudo npm install -g npm@11.5.2` which was
added in #34409. This causes issues with OIDC token permissions in the
release pipeline since `sudo` runs as a different user context.

## Expected Behavior

The publish workflow should use `npm install -g npm@11.5.2` without
`sudo`, matching the standard approach used elsewhere and avoiding
permission context issues during release.

## Related Issue(s)

Reverts #34409
2026-02-13 15:53:32 -05:00
Jack Hsu f5769f0bfb docs(misc): minor fixes for docs (#34449)
1. Consistent punctuation on intro page (periods at end of bullet
points).
2. Adjust AI detection for edge function.
2026-02-13 15:44:31 -05:00
Craigory Coppola 292a21319d feat(core): add --stdin to affected options (#34435)
- **feat(core): add `--stdin` to affected options**
- **fix(core): use newline-delimited stdin and add TTY guard for --stdin
option**

Supercedes #28770

Co-authored-by: @aaronccasanova

---------

Co-authored-by: Aaron Casanova <aaron.casanova@shopify.com>
Co-authored-by: nx-cloud[bot] <71083854+nx-cloud[bot]@users.noreply.github.com>
Co-authored-by: AgentEnder <AgentEnder@users.noreply.github.com>
2026-02-13 15:17:38 -05:00
Jason Jean c5e1bedca2 fix(repo): replace addnab/docker-run-action with direct docker run (#34448)
## Current Behavior

The publish workflow uses `addnab/docker-run-action@v3` which is based
on `docker:20.10` (Docker API 1.41). GitHub's `ubuntu-24.04` runners now
ship Docker Engine 28.x which requires minimum API version 1.44, causing
all 4 Linux Docker builds to fail:

```
docker: Error response from daemon: client version 1.41 is too old. Minimum supported API version is 1.44, please upgrade your client to a newer version.
```

Failed run: https://github.com/nrwl/nx/actions/runs/21961139962

## Expected Behavior

Linux Docker builds (x86_64-gnu, x86_64-musl, aarch64-gnu, aarch64-musl)
complete successfully using the host's modern Docker CLI.

https://github.com/nrwl/nx/actions/runs/21996143819

## Related Issue(s)

The `addnab/docker-run-action` repo is abandoned (last release March
2021, last commit May 2021) with open issues about this exact problem.
2026-02-13 12:42:30 -05:00
Jack Hsu c0540c8846 docs(misc): improve AX for getting started pages (#34410)
## Current Behavior

Getting started pages had overlapping content and unclear focus:
- `installation.mdoc` mentioned CNW and tutorials (belongs elsewhere)
- `start-new-project.mdoc` had manual setup option (belongs on
add-to-existing)
- `start-with-existing-project.mdoc` duplicated CI examples, editor
buttons, Nx Cloud walkthrough
- `intro.mdoc` mixed messaging - some "challenges" were polyrepo
problems

## Expected Behavior

Each page is now focused with no duplication:

### intro.mdoc
- Clear problem/solution structure following Turborepo's approach
- Problem: concise (builds get slow as codebase scales)
- Solution: caching, task orchestration, affected commands
- **Removed**: Polyrepo problems from challenge list (code sharing, lost
context)
- **Removed**: Lengthy deepdive callouts
- Net reduction: 67 deletions, 14 insertions

### installation.mdoc
- Global install (npm/brew/choco/apt) + verification step
- Local install (`nx init`) for existing repos
- Update instructions
- **Removed**: CNW mention, tutorials section, "More Documentation"

### start-new-project.mdoc
- Option 1: Create locally with templates (`create-nx-workspace`)
- Option 2: Create via Nx Cloud (browser-based)
- **Removed**: Manual setup option (that's for existing projects)
- Updated terminology: "presets" → "templates"

### start-with-existing-project.mdoc
- Focused: `nx init` → run tasks → see caching → explore graph
- Links to other pages instead of duplicating content
- **Removed**: CI config examples, Nx Cloud walkthrough, editor buttons

### editor-setup.mdoc
- Added problem hook explaining why editor integration matters
- Clarified Neovim is community-maintained

### ai-setup.mdoc
- Added problem hook about AI hallucination without workspace context
- Explained MCP acronym (Model Context Protocol)
- Clarified "Ralph Wiggum loop" terminology

### sidebar.mts
- Reordered to match natural flow: Installation → Start New/Add Existing
→ Editor/AI

## Related Issue(s)

Closes DOC-405

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: nx-cloud[bot] <71083854+nx-cloud[bot]@users.noreply.github.com>
Co-authored-by: jaysoo <jaysoo@users.noreply.github.com>
2026-02-13 11:09:21 -05:00
Craigory Coppola ece9b5bf4a fix(core): remove shellapi from winapi featureset to minimize AV false positives (#34208)
## Current Behavior
There's a chance that windows can falsely flag our native binaries as a
threat. We do not use the shellapi feature from winapi.

## Expected Behavior
We hope that removing this API doesn't break things, and the threat
messaging goes away

## Related Issue(s)
<!-- Please link the issue being fixed so it gets closed when this is
merged. -->

Fixes #https://github.com/nrwl/nx/issues/34186

---------

Co-authored-by: nx-cloud[bot] <71083854+nx-cloud[bot]@users.noreply.github.com>
2026-02-12 19:32:58 -05:00
Craigory Coppola 51420790c3 chore(repo): improve copy-built-package script (#34432)
Makes copy-built-package script a bit more ergonomic and discoverable.
Adds some interactive UI for picking package / repo if they are not
specified.
2026-02-12 16:49:51 -05:00
Craigory Coppola c407de6e2b fix(core): hitting [1] or [2] should remove pinned panes if they match the current task (#34433)
## Current Behavior

Pressing `[1]` or `[2]` on a task that's already pinned to that slot
**focuses** the pane instead of unpinning it. This means there's no way
to unpin a single pane via keyboard — you can only nuke everything with
`[0]`.

This regression was introduced in #34175, which fixed a real problem:
pressing Enter on an already-pinned task would unpin it, leaving focus
on an invisible pane (a ghost pane — you're staring at nothing but the
TUI thinks you're looking at output). The fix was to make the "already
pinned to this pane" branch focus instead of unpin. The problem is that
`[1]`, `[2]`, and Enter all flowed through the same function
(`assign_current_task_to_pane`), so changing behavior for Enter changed
it for everyone. One lock got swapped out, and every door started
behaving the same way.

## Expected Behavior

`[1]` and `[2]` are **toggles**: press once to pin, press again to
unpin. Enter is a **display** action: show me this task's output and put
my cursor there — if it's already visible somewhere, just take me to it.

After this change:

- **`[1]` / `[2]`** on an already-pinned task → unpins it (pane
disappears, layout adjusts, focus returns to task list if no panes
remain)
- **Enter** on an already-pinned task → focuses whichever pane it's in
(even if it's in pane 2 and you'd normally expect pane 1)
- **Init** (startup restore of pinned tasks) → pure assignment, no
toggling, no focusing

## Approach

The old code had one function trying to serve three masters. Rather than
adding a flag parameter (`should_toggle: bool`) — which would just be a
boolean that lies about its intentions at every call site — the function
was split along the actual semantic boundaries:

| Function | Used by | "Already pinned here" behavior |
|---|---|---|
| `toggle_current_task_in_pane` | `[1]` / `[2]` keys | **Unpin** (toggle
off) |
| `assign_current_task_to_pane` | `init()` | No-op (task is where it
should be) |
| `display_and_focus_current_task_in_terminal_pane` | Enter key |
**Focus** the existing pane |

The shared logic — exiting spacebar mode, moving a task between panes,
fresh-pinning — lives in two small helpers (`exit_spacebar_and_pin`,
`move_or_pin_selection`) that both `toggle` and `assign` delegate to.
The only code that differs is the "what do we do when it's already
here?" branch, which is exactly the part that *should* differ.

**Why not keep one function with a mode parameter?** Because the three
behaviors aren't variations of the same action — they're genuinely
different user intents. A toggle is "I changed my mind." A focus is
"Take me there." An assignment is "Put this here." Encoding that as an
enum parameter just moves the branching somewhere less obvious and makes
the call sites harder to read. The function names now document the
intent at the point of use, and there's no shared state to accidentally
couple.

**Why does Enter check all panes, not just pane 0?** Because if you
pinned a task to pane 2 via `[2]` and then press Enter on it, the least
surprising thing is to jump to where it already lives — not to silently
duplicate it into pane 1 or ignore you. The task is already on screen;
Enter means "show me."

## Related Issue(s)

Fixes the regression introduced by #34175.
2026-02-12 16:21:29 -05:00
Jack Hsu 950265fc8c feat(misc): lock in CNW variant 2 with deferred connection (#34416)
## Current Behavior

CNW (Create Nx Workspace) has A/B testing logic that randomly selects
between variants 0, 1, and 2 for the Nx Cloud connection flow. Each
variant shows different prompts and banners.

## Expected Behavior

Lock in variant 2 as the permanent behavior:
- **No cloud prompt** - users are not asked about Nx Cloud during
workspace creation
- **Deferred connection** - no `nxCloudId` is written to `nx.json` (uses
`skipCloudConnect: true`)
- **Variant 2 banner** - shows "Enable remote caching and automatic
fixes when CI fails" with a link to complete setup later

### Changes
- Simplified `ab-testing.ts` - removed caching, random selection;
`getFlowVariant()` always returns `'2'`
- `shouldShowCloudPrompt()` always returns `false`
- `determineNxCloudV2()` returns `'github'` with `skipCloudConnect:
true` for deferred connection
- Removed variant 1 banner logic from `messages.ts`
- Updated tests to reflect the locked-in behavior

## Demo

https://www.loom.com/share/7f688eed6052428cbe91dd9db837cbbd

## Related Issue(s)

Closes CLOUD-4255

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-12 16:05:28 -05:00
Benjamin Cabanes 28fea0db0c docs(nx-dev): replace global ID with deterministic target ID for HBST (#34431)
Simplified form targeting by replacing the global `reactHubspotForm` ID
with a deterministic `targetId` that incorporates portal, form, and
calendly IDs. This improves scalability and avoids potential ID
collisions.
2026-02-12 14:03:02 -05:00
Benjamin Cabanes b5c3663126 docs(nx-dev): add back inline script (#34429) 2026-02-12 12:56:17 -05:00
MaxKless a757f40d83 fix(maven): correctly map between maven locators and nx project names (#34366) 2026-02-13 01:42:31 +09:00
Jack Hsu 7a4e052533 chore(misc): add banner content monitor workflow (#34417)
## Current Behavior

When banner content changes in Framer, the nx-docs and nx-dev sites need
to be manually redeployed to pick up the new content.

## Expected Behavior

A scheduled workflow monitors the banner URL every 15 minutes and
automatically triggers Netlify production deploys when content changes.

## How it works

1. Fetches `BANNER_URL` content (from repository variable)
2. Computes SHA256 hash
3. Compares to cached hash from previous run
4. If different → triggers both Netlify deploys, updates cache
5. If unchanged → no-op

## Required Setup

1. **Repository variable** (`Settings → Secrets and variables → Actions
→ Variables`):
   - `BANNER_URL` = Framer banner API URL

2. **Repository secret** (`Settings → Secrets and variables → Actions →
Secrets`):
   - `NETLIFY_AUTH_TOKEN` = Netlify personal access token

## Related Issue(s)

Fixes DOC-405
2026-02-12 10:37:47 -05:00
Juri 9a57042cbe docs(nx-dev): add Nx AI agent skills blog post 2026-02-12 16:28:57 +01:00
Steven Nance 754b01a066 feat(core): add negation pattern support for plugin include/exclude (#34160)
<!-- Please make sure you have read the submission guidelines before
posting an PR -->
<!--
https://github.com/nrwl/nx/blob/master/CONTRIBUTING.md#-submitting-a-pr
-->

<!-- Please make sure that your commit message follows our format -->
<!-- Example: `fix(nx): must begin with lowercase` -->

<!-- If this is a particularly complex change or feature addition, you
can request a dedicated Nx release for this pull request branch. Mention
someone from the Nx team or the `@nrwl/nx-pipelines-reviewers` and they
will confirm if the PR warrants its own release for testing purposes,
and generate it for you if appropriate. -->

## Current Behavior
Negation patterns are ignored in plugin configuration for the `include`
and `exclude` properties.

## Expected Behavior

- Negation patterns should work in the same way that they do for other
`include`/`exclude` configurations

**Example: Excluding all e2e projects except one**

```jsonc
// nx.json
{
  "plugins": [
    {
      "plugin": "@nx/jest/plugin",
      "exclude": ["**/*-e2e/**/*", "!**/toolkit-workspace-e2e/**/*"],
    },
  ],
}
```

This will exclude all e2e projects except `toolkit-workspace-e2e`.

**Example: Including packages except legacy ones**

```jsonc
// nx.json
{
  "plugins": [
    {
      "plugin": "@nx/vite/plugin",
      "include": ["packages/**/*", "!packages/legacy/**/*"],
    },
  ],
}
```

**How negation patterns work:**

- Patterns are processed in order from first to last
- A pattern starting with `!` removes files from the match set
- A pattern without `!` adds files to the match set
- The last matching pattern determines if a file is included
- If the first pattern is a negation, all files are matched initially

---------

Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-12 16:03:20 +01:00
Colum Ferry 5a1735b041 feat(misc): update PLUGIN.md files to help agents verification (#34379)
## Current Behavior
There is currently no plugin.md file for Gradle.
Other plugin.md files can be improved

## Expected Behavior
Add plugin.md file for Gradle to aid with verification with Agents.
Add plugin.md file for Vite for workspaces that have not migrated to
@nx/vitest.

## Related Issue(s)

CLOSES NXC-3843

---------

Co-authored-by: nx-cloud[bot] <71083854+nx-cloud[bot]@users.noreply.github.com>
Co-authored-by: Coly010 <Coly010@users.noreply.github.com>
2026-02-12 12:25:10 +01:00
Josh VanAllen a0ff232ad7 feat(testing): add cacheDir option to playwright executor (#34413)
## Current Behavior

The Playwright executor does not support configuring a custom cache
directory for Playwright's internal cache (browser binaries, etc.).
Users who need to control where Playwright stores its cache, for example
in CI environments with specific disk constraints like not being able to
DTE tasks or shared caching setups, have no way to set this through the
executor configuration.

## Expected Behavior

A new `cacheDir` option is available on the Playwright executor. When
provided, it sets the `PWTEST_CACHE_DIR` environment variable on the
forked Playwright process, allowing users to control where Playwright
stores its internal cache.
  ```json
  {
    "targets": {
      "e2e": {
        "executor": "@nx/playwright:playwright",
        "options": {
          "cacheDir": "/tmp/playwright-cache"
        }
      }
    }
  }
  ```

## Related Issue(s)

Replaces #34397

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-11 17:36:17 -05:00
Jack Hsu 17f2f1bdc6 docs(misc): clarify security email usage in SECURITY.md (#34411)
## Current Behavior

The SECURITY.md file does not clarify what types of reports should be
sent to the security email, leading to reports about outdated
dependencies and vulnerability scanner output.

## Expected Behavior

The file now clarifies that the security email is for demonstrable,
verified vulnerabilities in the Nx codebase itself, not for:
- Outdated dependency reports
- Dependencies with CVEs that don't directly affect Nx
- General vulnerability scanner output

## Related Issue(s)

Fixes NXC-3898

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-11 15:08:20 -05:00
Jason Jean e48f2f3a8c fix(repo): use sudo for global npm install in publish workflow (#34409)
## Current Behavior

The `npm install -g npm@11.5.2` step in the publish workflow fails with
`EACCES: permission denied, mkdir '/usr/local/share/man/man5'` on newer
GitHub Actions runner images.

## Expected Behavior

The global npm install step completes successfully regardless of runner
image permissions on `/usr/local/share/man/`.

## Related Issue(s)

This is a known issue with GitHub Actions runners:
https://github.com/actions/runner-images/issues/9644
2026-02-11 14:49:43 -05:00
Colum Ferry 7785eae516 feat(core): extract sandbox detection into reusable utility (#34408)
Add isSandbox() utility that checks for sandbox environment variables
(SANDBOX_RUNTIME, GEMINI_SANDBOX, CODEX_SANDBOX, CURSOR_SANDBOX) and
use it to disable the daemon and plugin isolation in sandbox
environments.
2026-02-11 18:33:17 +00:00
Miroslav Jonaš 28c5d95964 fix(nx-dev): clarify project linking for workspaces (#34405)
<!-- Please make sure you have read the submission guidelines before
posting an PR -->
<!--
https://github.com/nrwl/nx/blob/master/CONTRIBUTING.md#-submitting-a-pr
-->

<!-- Please make sure that your commit message follows our format -->
<!-- Example: `fix(nx): must begin with lowercase` -->

<!-- If this is a particularly complex change or feature addition, you
can request a dedicated Nx release for this pull request branch. Mention
someone from the Nx team or the `@nrwl/nx-pipelines-reviewers` and they
will confirm if the PR warrants its own release for testing purposes,
and generate it for you if appropriate. -->

## Current Behavior
<!-- This is the behavior we have today -->

## Expected Behavior
<!-- This is the behavior we should expect with the changes in this PR
-->

## Related Issue(s)
<!-- Please link the issue being fixed so it gets closed when this is
merged. -->

Fixes #

---------

Co-authored-by: nx-cloud[bot] <71083854+nx-cloud[bot]@users.noreply.github.com>
Co-authored-by: meeroslav <meeroslav@users.noreply.github.com>
2026-02-11 16:34:13 +00:00
Caleb Ukle 0e53c3f3d5 fix(nx-dev): add missing nx-cloud intro in sidebar (#34403) 2026-02-11 15:36:12 +00:00
Colum Ferry 6bf8c4693f feat(core): handle agentic sandboxing (#34402)
## Current Behavior
Running ai agents in sandbox mode causes issues with Nx's daemon and
plugin isolation

## Expected Behavior
Running ai agents in sandbox mode should work

## Related Issue(s)

CLOSES NXA-828
2026-02-11 15:12:49 +00:00
Philip Fulcher 8e3cff657b docs(nx-dev): add broadcom success story (#34393) 2026-02-11 09:36:42 -05:00
Colum Ferry 15d508e814 feat(core): add nxVersion to meta in shortUrl for cnw (#34401)
## Current Behavior
We do not include NxVersion when creating short urls.

## Expected Behavior
Include NxVersion when creating short urls. 

## Related Issue(s)

CLOSES NXC-3879
2026-02-11 09:27:03 -05:00
Craigory Coppola 6570674abf fix(core): handle dangling symlinks during cache restore (#34396)
When cache outputs include both glob patterns and directory patterns
containing symlinks, the cache restore fails with EEXIST (os error 17).
This happens because `fs_extra::remove_items` silently skips dangling
symlinks (since `is_dir()`/`is_file()` follow links and return false),
leaving stale symlinks that cause `symlink()` to fail.

The fix makes symlink creation idempotent by checking for and removing
any existing symlink at the destination before creating a new one, using
`symlink_metadata()` which correctly detects dangling symlinks.

Fixes #34013
2026-02-10 17:41:18 -05:00
Jason Jean 2d71d9ac65 fix(maven): use module-level variable for cache transfer between createNodes and createDependencies (#34386)
## Current Behavior

The Maven plugin's `createNodes` and `createDependencies` functions both
independently compute a hash of all pom.xml directories, then use that
hash to look up cached Maven analysis data from disk. When Maven
projects have `<includes>` or `<excludes>` in their plugin config, the
hash can differ between the two calls, causing `createDependencies` to
fail to find the data that `createNodes` stored.

## Expected Behavior

`createDependencies` reliably receives the Maven analysis data from
`createNodes` regardless of hash differences, by reading it from a
module-level variable instead of re-hashing and looking it up from disk.

This matches the pattern already used by the Gradle plugin
(`getCurrentGradleReport`).

## Related Issue(s)
2026-02-10 16:27:51 -05:00
Leosvel Pérez Espinosa f43d2028ff fix(core): make runtime cache key deterministic (#34390)
## Current Behavior

Runtime cache keys could be nondeterministic because the order of
environment variables varied, leading to inconsistent cache hits across
runs.

## Expected Behavior

Runtime cache keys are deterministic regardless of the insertion order
of env variables, improving cache stability.
2026-02-10 15:45:30 -05:00
Leosvel Pérez Espinosa 6c9f0cb46d fix(core): avoid dropping unrelated continuous deps in makeAcyclic (#34389)
## Current Behavior

Cycles in the task graph could remove unrelated `continuousDependencies`
when the cycle exists only in `dependencies`, leading to missing
continuous task edges.

## Expected Behavior

Cycle removal only removes the specific cyclic edge from the list where
it appears, preserving unrelated continuous dependencies.
2026-02-10 15:44:26 -05:00
Caleb Ukle bd13929de8 fix(nx-dev): improve plugin registry visibility (#34395)
- **fix(nx-dev): make sure "plugin registry" shows up in search**
- search ranking will be re-evaled after we work through more content
updates
<img width="768" height="1406" alt="image"
src="https://github.com/user-attachments/assets/e7ca2aff-7daf-417b-ad96-ba6722480432"
/>

- **docs(nx-dev): add plugin registry to footer**
<img width="1076" height="405" alt="image"
src="https://github.com/user-attachments/assets/4c93c5ec-1f64-4cd6-8f88-9347d5009ac9"
/>
2026-02-10 13:13:54 -06:00
Brett Burley f5a7ea1606 fix(core): clean up stale socket files before listening (#34236)
## Current Behavior

When running Nx tasks in CI environments (e.g., Buildkite) where the
host's /tmp is mounted to containers, intermittent EADDRINUSE errors
occur in PseudoIPCServer.init(). This happens because:

1. PseudoIPCServer doesn't clean up its Unix socket file before calling
listen()
2. ForkedProcessTaskRunner.createPseudoTerminal() instantiates
PseudoTerminal directly instead of using the createPseudoTerminal()
helper, bypassing shutdown callback registration

When a new container starts with the same PID as a previous run (PID
recycling), it generates the same socket path and hits EADDRINUSE
because the stale socket file still exists.

## Expected Behavior

No EADDRINUSE errors should occur. The PseudoIPCServer should
defensively remove any stale socket file before attempting to listen,
similar to how the daemon server handles this.

## Related Issue(s)

Fixes #34233
2026-02-10 13:36:34 -05:00
Benjamin Cabanes 9c42292ed8 docs(nx-dev): remove Cookiebot & GA integration, migrate all events to GTM (#34384)
Streamlined analytics tracking by removing Cookiebot and direct GA
(gtag.js) integrations. Consolidated event logging through GTM's
dataLayer for consistency and maintenance simplicity.
2026-02-10 12:17:47 -05:00
Altan Stalker ec0f51ed75 chore(core): enable cloud experimental polling (#34394)
Updated CI behavior
2026-02-10 12:05:20 -05:00
Leosvel Pérez Espinosa 5ae53ecae8 fix(core): use a consistent batch id between scheduler and task runner (#34392)
## Current Behavior

Batch IDs are generated in two places: the task scheduler uses an
incremental counter (`executorName N`) while the forked process task
runner generates its own using the process PID (`executorName-pid`).
This means the batch ID registered in metrics doesn't match the one used
everywhere else.

## Expected Behavior

Batch IDs are only created by the task scheduler. The forked process
task runner uses the scheduler-assigned ID to ensure consistency across
the system.
2026-02-10 11:33:14 -05:00
MaxKless 5066511576 fix(core): make sure that mcp args aren't overridden when running configure-ai-agents (#34381)
## Current Behavior
right now if users modify their mcp params like `--minimal`, we will
override them on `configure-ai-agents`

## Expected Behavior
We want to bring users up to latest without overriding their valid
configurations
2026-02-10 14:24:09 +01:00
Benjamin Staneck 0b6961b0d7 feat(core): update formatting of agent rules documentation (#33356) 2026-02-10 22:22:41 +09:00
Caleb Ukle 089e111fcc docs(nx-cloud): update info about GH permissions (#34380)
https://deploy-preview-34380--nx-docs.netlify.app/docs/enterprise/single-tenant/custom-github-app#configure-permissions-for-the-github-app

---------

Co-authored-by: nx-cloud[bot] <71083854+nx-cloud[bot]@users.noreply.github.com>
Co-authored-by: barbados-clemens <barbados-clemens@users.noreply.github.com>
2026-02-09 12:09:17 -06:00
Jason Jean 93fddd14de chore(repo): update nx to 22.5.0-beta.5 (#34371)
Updating Nx from 22.5.0-beta.4 to 22.5.0-beta.5
2026-02-09 13:00:52 -05:00
Kai Gritun f1873b27a8 fix(core): use --lockfile-only for Bun updateLockFile (#34375)
## Current Behavior

The Bun package manager config uses `--frozen-lockfile` for
`updateLockFile`:

```typescript
updateLockFile: 'bun install --frozen-lockfile',
```

However, `--frozen-lockfile` **prevents** changes to the lockfile,
causing `nx release` to fail when trying to update the lockfile after
version bumps.

## Expected Behavior

Use `--lockfile-only` which generates/updates the lockfile without
installing dependencies:

```typescript
updateLockFile: 'bun install --lockfile-only',
```

This is consistent with other package managers:
- npm: `npm install --package-lock-only`
- pnpm: `pnpm install --lockfile-only`
- yarn berry: `yarn install --mode update-lockfile`

## Background

When Bun support was added in PR #22602 (April 2024), `--lockfile-only`
didn't exist in Bun. Bun has since added this flag.

Closes #34344

Co-authored-by: Kai Gritun <kai@kaigritun.com>
2026-02-09 10:08:19 -05:00
Jason Jean 81c157d063 fix(repo): align pnpm version in CI workflows with package.json (#34370)
## Current Behavior

Several GitHub Actions workflows hardcode pnpm version `10.11.1`, while
`package.json` specifies `pnpm@10.28.2` in the `packageManager` field.
This causes CI failures with:

```
Error: Multiple versions of pnpm specified:
  - version 10.11.1 in the GitHub Action config with the key "version"
  - version pnpm@10.28.2 in the package.json with the key "packageManager"
```

## Expected Behavior

All pnpm version references across CI workflows should match the
`packageManager` field in `package.json` (`10.28.2`).

## Related Issue(s)

N/A — Fixing CI breakage from version mismatch.

## Changes

Updated pnpm version from `10.11.1` → `10.28.2` in:
- `.github/workflows/npm-audit.yml` — `pnpm/action-setup` version
- `.github/workflows/publish.yml` — `PNPM_VERSION` env var and FreeBSD
install
- `.github/workflows/issue-notifier.yml` — `pnpm/action-setup` version
- `.github/workflows/generate-embeddings.yml` — `pnpm/action-setup`
version
2026-02-06 19:45:33 -05:00
Jason Jean 1f5520ebb1 fix(core): add missing FileType import for Windows watcher build (#34369)
## Current Behavior

The Windows build (`aarch64-pc-windows-msvc`) fails to compile with:

```
error[E0433]: failed to resolve: use of undeclared type `FileType`
  --> packages\nx\src\native\watch\types.rs:128:55
```

The `FileType` type is used inside a `#[cfg(target_os = "windows")]`
block but was not imported.

## Expected Behavior

The Windows build compiles successfully. The `FileType` import is scoped
inside the `#[cfg(target_os = "windows")]` block (matching the existing
pattern in the macOS block) so there are no unused imports on any
platform.

## Related Issue(s)

N/A — build breakage discovered during CI publish workflow.
2026-02-06 18:47:18 -05:00
Jason Jean 0aef1ef26f fix(core): reduce daemon inotify watch count by upgrading watchexec (#34329)
## Current Behavior

The daemon's file watcher uses watchexec 3.0.1 which hardcodes
`RecursiveMode::Recursive` when registering inotify watches. This means
**every** directory gets an inotify watch — including all of
`node_modules`, `.git`, and other ignored trees.

On a typical workspace with a large `node_modules`, this can consume
thousands of inotify watches, eating kernel memory and CPU. The
`WatchFilterer` only filters **events** after watches are already
registered — the watches themselves are never prevented.

## Expected Behavior

Only non-ignored directories (workspace source code) get inotify
watches. Ignored directories like `node_modules`, `.git`, `.nx/cache`,
`.nx/workspace-data`, and `.yarn/cache` are skipped entirely at the
watch registration level.

This dramatically reduces:
- **inotify watch count** (from thousands to hundreds)
- **Memory usage** (each watch consumes kernel memory)
- **CPU overhead** (fewer watches = less kernel bookkeeping)

### How it works

- Upgraded watchexec 3.0.1 → 8.0.1 which supports
`WatchedPath::non_recursive()`
- Added `create_watch_walker()` using `ignore::WalkBuilder` (same
pattern as `walker.rs`) to enumerate only non-ignored directories
- Each directory is watched with `NonRecursive` mode — like putting
security cameras only in the rooms you care about instead of every room
in the building
- New directories created at runtime are dynamically added to the watch
set via the `on_action` handler
- Event-level filtering via `WatchFilterer` is unchanged — same behavior
for gitignore/nxignore patterns

### macOS Support for Dynamic Directory Registration

The initial implementation worked on Linux and Windows but failed tests
on macOS because macOS FSEvents doesn't always provide the same
`FileEventKind` tags as Linux inotify or Windows ReadDirectoryChangesW.

**Three changes to support macOS:**

1. **watcher.rs**: On macOS, check all events for directory creation
(not just events with specific FileEventKind tags) and verify via
filesystem
2. **types.rs**: Filter directory events from JavaScript callbacks on
macOS (similar to Windows behavior)
3. **watch_filterer.rs**: Allow macOS directory events (`Create(Folder)`
and `Modify(Metadata)`) through the filter so the action handler can
register them

All changes use `#[cfg(target_os = "macos")]` for compile-time
conditional compilation, so Linux/Windows behavior is completely
unchanged and there's zero runtime overhead.

### Additional notes

- Pinned `serde` to `<1.0.220` because serde 1.0.220+ moved `__private`
to `serde_core`, breaking `swc_common 0.31.22`
- No TypeScript changes — the napi interface is identical
- `watch_filterer.rs`, `types.rs`, `utils.rs` required no changes (APIs
are compatible)

## Related Issue(s)
Fixes https://github.com/nrwl/nx/issues/33781
Fixes https://github.com/nrwl/nx-console/issues/2468
<!-- No specific issue linked yet -->

---------

Co-authored-by: nx-cloud[bot] <71083854+nx-cloud[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-06 17:21:19 -05:00
Louie Weng 693f75149a chore(repo): re-enable gradle e2e tests (#34357)
<!-- Please make sure you have read the submission guidelines before
posting an PR -->
<!--
https://github.com/nrwl/nx/blob/master/CONTRIBUTING.md#-submitting-a-pr
-->

<!-- Please make sure that your commit message follows our format -->
<!-- Example: `fix(nx): must begin with lowercase` -->

<!-- If this is a particularly complex change or feature addition, you
can request a dedicated Nx release for this pull request branch. Mention
someone from the Nx team or the `@nrwl/nx-pipelines-reviewers` and they
will confirm if the PR warrants its own release for testing purposes,
and generate it for you if appropriate. -->

## Current Behavior
<!-- This is the behavior we have today -->

## Expected Behavior
<!-- This is the behavior we should expect with the changes in this PR
-->

Re-enabling tests and putting back kotlin e2e tests for gradle.

## Related Issue(s)
<!-- Please link the issue being fixed so it gets closed when this is
merged. -->

Fixes #
2026-02-06 13:24:48 -08:00
Craigory Coppola 5e4dd04b66 fix(core): only detect flaky tasks for cacheable tasks (#33994)
## Current Behavior

Flaky task detection warns about all tasks that have different exit
codes for the same hash, including non-cacheable tasks. This is
misleading because the flaky task warning message points users to Nx
Cloud's flaky task retry feature, which is only relevant for cached
tasks.

## Expected Behavior

Flaky task detection should only consider tasks where `task.cache ===
true`, making the warning more meaningful and avoiding noise for
non-cacheable tasks.

## Related Issue(s)

N/A - Internal improvement

## Changes Made

###
`packages/nx/src/tasks-runner/life-cycles/task-history-life-cycle.ts`
1. Added `cacheable: boolean` to the `TaskRun` interface
2. In `endTasks`, now tracks `cacheable: taskResult.task.cache === true`
for each task
3. In `endCommand`, filters to only check flaky tasks among cacheable
tasks

###
`packages/nx/src/tasks-runner/life-cycles/task-history-life-cycle-old.ts`
1. Added `cacheableHashes: Set<string>` to track which task hashes are
cacheable
2. In `endTasks`, tracks cacheable tasks by adding their hash to the set
3. In `endCommand`, only checks for flaky tasks among cacheable task
hashes

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-06 15:39:43 -05:00
Louie Weng 8280910e48 docs(gradle): add compat table and target name prefix (#34359)
<!-- Please make sure you have read the submission guidelines before
posting an PR -->
<!--
https://github.com/nrwl/nx/blob/master/CONTRIBUTING.md#-submitting-a-pr
-->

<!-- Please make sure that your commit message follows our format -->
<!-- Example: `fix(nx): must begin with lowercase` -->

<!-- If this is a particularly complex change or feature addition, you
can request a dedicated Nx release for this pull request branch. Mention
someone from the Nx team or the `@nrwl/nx-pipelines-reviewers` and they
will confirm if the PR warrants its own release for testing purposes,
and generate it for you if appropriate. -->

## Current Behavior
<!-- This is the behavior we have today -->

## Expected Behavior
<!-- This is the behavior we should expect with the changes in this PR
-->

A new version compatibility table is added to help users understand
which versions of the Gradle plugin work with which versions of the Nx
plugin. The targetNamePrefix configuration option is now documented with
an explanation of its use case in polyglot workspaces where target name
collisions may occur.

## Related Issue(s)
<!-- Please link the issue being fixed so it gets closed when this is
merged. -->

Fixes #
2026-02-06 12:35:33 -08:00
Craigory Coppola 37a69f0c9d feat(core): eagerly shutdown plugins that don't provide later hooks (#34253)
# Plugin Isolation Architecture

## 1. Plugin Loading Flow

### 1a. Entry Point - Isolation Decision

```mermaid
flowchart TD
    Start([getPlugins called]) --> CheckIsolation{Isolation<br/>enabled?}
    CheckIsolation -->|Yes| LoadIsolated[loadIsolatedNxPlugin]
    CheckIsolation -->|No| LoadInProcess[loadNxPluginInProcess]
    LoadIsolated --> IsolatedPath([See: Isolated Loading])
    LoadInProcess --> InProcessPath([See: In-Process Loading])
```

### 1b. Isolated Plugin Loading

```mermaid
flowchart TD
    subgraph Main["Main Process"]
        Start([loadIsolatedNxPlugin]) --> CheckCache{In cache?}
        CheckCache -->|Yes| ReturnCached([Return cached promise])
        CheckCache -->|No| StaticLoad[IsolatedPlugin.load]
        StaticLoad --> Resolve[resolveNxPlugin<br/>find plugin path]
        Resolve --> SpawnWorker[spawn child process]
    end

    SpawnWorker -.->|"start process"| WorkerStart

    subgraph Worker["Worker Process (plugin-worker.ts)"]
        WorkerStart([process starts]) --> CreateServer[create Unix socket server]
        CreateServer --> Listen[listen for connections]
        Listen --> WaitForConnect[wait for main process]
        WaitForConnect --> HandleLoad[receive 'load' message]

        subgraph InProcess["In-Process Loading (same as 1c)"]
            HandleLoad --> RequirePlugin[require plugin module]
            RequirePlugin --> NormalizePlugin[normalizeNxPlugin]
        end

        NormalizePlugin --> SendLoadResult[send 'loadResult'<br/>with hook capabilities]
        SendLoadResult --> WaitForMessages[wait for hook messages<br/>or socket close]
        WaitForMessages --> HandleHook{message<br/>received?}
        HandleHook -->|hook message| ExecuteHook[call plugin.hook]
        ExecuteHook --> SendResult[send result]
        SendResult --> WaitForMessages
        HandleHook -->|socket closed| Cleanup[cleanup & exit]
    end

    subgraph Main2["Main Process (continued)"]
        ConnectSocket[connect via<br/>Unix socket] --> SendLoad[send 'load' message]
        SendLoad --> WaitLoad[wait for 'loadResult']
        WaitLoad --> SetupHooks[setupHooks<br/>create lifecycle manager]
        SetupHooks --> CheckGraphHooks{Has graph<br/>phase hooks?}
        CheckGraphHooks -->|No| EarlyShutdown[socket.end<br/>shutdown worker]
        CheckGraphHooks -->|Yes| KeepAlive[keep worker alive]
        EarlyShutdown --> Done([Plugin ready])
        KeepAlive --> Done
    end

    SpawnWorker --> ConnectSocket
    SendLoadResult -.->|"loadResult"| WaitLoad
    EarlyShutdown -.->|"socket close"| Cleanup
```

### 1c. In-Process Plugin Loading

```mermaid
flowchart TD
    Start([loadNxPluginInProcess]) --> Resolve[resolveNxPlugin]
    Resolve --> Require[require plugin module]
    Require --> Normalize[normalizeNxPlugin<br/>wrap hooks]
    Normalize --> Done([Plugin ready])
```

## 2. Hook Execution Flow

### 2a. Isolated Hook Execution

```mermaid
flowchart TD
    Start([hook called<br/>e.g. createNodes]) --> EnsureAlive{_alive?}
    EnsureAlive -->|No| Restart[spawnAndConnect<br/>restart worker]
    Restart --> SetAlive[_alive = true]
    SetAlive --> EnsureAlive

    EnsureAlive -->|Yes| EnterHook[lifecycle.enterHook<br/>increment session count]
    EnterHook --> SendRequest[sendRequest<br/>over socket]
    SendRequest --> WaitResponse[wait for response<br/>with timeout]

    WaitResponse --> CheckSuccess{success?}
    CheckSuccess -->|No| ExitHookError[lifecycle.exitHook]
    ExitHookError --> ThrowError[throw error]

    CheckSuccess -->|Yes| ExitHook[lifecycle.exitHook]
    ExitHook --> CheckShutdown{should<br/>shutdown?}
    CheckShutdown -->|Yes| Shutdown[shutdown worker]
    CheckShutdown -->|No| Return([return result])
    Shutdown --> Return
```

### 2b. Shutdown Decision Logic

```mermaid
flowchart TD
    Start([exitHook called]) --> IsLastHook{Last hook<br/>in phase?}
    IsLastHook -->|No| NoShutdown1([return false])

    IsLastHook -->|Yes| CheckSessions{sessionCount<br/>== 0?}
    CheckSessions -->|No| NoShutdown2([return false<br/>other callers active])

    CheckSessions -->|Yes| CheckLaterPhases{Has later<br/>active phases?}
    CheckLaterPhases -->|Yes| NoShutdown3([return false<br/>needed later])
    CheckLaterPhases -->|No| YesShutdown([return true<br/>safe to shutdown])
```

## 3. Developer Workflow: Adding/Modifying Plugin Hooks

### Step 1: Design Public API

```mermaid
flowchart TD
    A1[public-api.ts] --> A2[Define context type<br/>e.g. MyHookContext]
    A2 --> A3[Export new types]
    A3 --> A4[loaded-nx-plugin.ts]
    A4 --> A5[Add hook to<br/>LoadedNxPlugin interface]
```

### Step 2: Define Message Types

```mermaid
flowchart TD
    B1[messaging.ts] --> B2[Add entry to PluginMessageDefs]
    B2 --> B3[Define payload and result types]
    B3 --> B4[Add to MESSAGE_TYPES array]
    B4 --> B5[Add to RESULT_TYPES array]
```

The messaging system uses a unified `DefineMessages` pattern. To add a
new message:

```typescript
// In PluginMessageDefs, add a new entry:
type PluginMessageDefs = DefineMessages<{
  // ... existing messages ...

  myHook: {
    payload: {
      context: MyHookContext;
    };
    result:
      | { success: true; data: MyResultData }
      | { success: false; error: Error };
  };
}>;
```

The individual message/result types (`PluginWorkerMyHookMessage`,
`PluginWorkerMyHookResult`)
are automatically derived. Export them if needed for external use:

```typescript
export type PluginWorkerMyHookMessage = MessageOf<PluginMessageDefs, 'myHook'>;
export type PluginWorkerMyHookResult = ResultOf<PluginMessageDefs, 'myHook'>;
```

### Step 3: Handle in Worker Process

```mermaid
flowchart TD
    C1[plugin-worker.ts] --> C2[Add handler in<br/>consumeMessage]
    C2 --> C3["Call plugin.myHook()"]
    C3 --> C4[Return result payload]
```

Handlers return just the result payload - the infrastructure wraps it
automatically:

```typescript
// In consumeMessage handlers:
myHook: async ({ context }) => {
  try {
    const data = await plugin.myHook(context);
    return { success: true as const, data };
  } catch (e) {
    return { success: false as const, error: createSerializableError(e) };
  }
},
```

### Step 4: Update Load Result

```mermaid
flowchart TD
    D1[messaging.ts] --> D2[Add hasMyHook to<br/>load.result in PluginMessageDefs]
    D2 --> D3[plugin-worker.ts]
    D3 --> D4[Populate hasMyHook<br/>in load handler]
```

### Step 5: Wire Up IsolatedPlugin

```mermaid
flowchart TD
    E1[isolated-plugin.ts] --> E2[Add hook property<br/>to class]
    E2 --> E3[Update LoadResultPayload<br/>type export]
    E3 --> E4[Add to registeredHooks<br/>array in setupHooks]
    E4 --> E5[Add wrapped hook<br/>implementation]
    E5 --> E6["wrap('myHook', async (ctx) => {<br/>  sendRequest('myHook', { context: ctx })<br/>})"]
```

### Step 6: Update Lifecycle Phases (if needed)

```mermaid
flowchart TD
    F1{New phase<br/>needed?} -->|Yes| F2[plugin-lifecycle-manager.ts]
    F2 --> F3[Add phase to<br/>HOOKS_BY_PHASE]
    F1 -->|No| F4[Add hook to existing<br/>phase array in HOOKS_BY_PHASE]
```

### Step 7: Add Tests

```mermaid
flowchart TD
    G1[isolated-plugin.spec.ts] --> G2[Test hook registration]
    G2 --> G3[Test hook execution]
    G3 --> G4[Test restart behavior]
    G4 --> G5[plugin-lifecycle-manager.spec.ts]
    G5 --> G6[Test phase transitions<br/>with new hook]
    G6 --> G7[Test shutdown decisions]
```

## File Reference

| File | Purpose |
| ----------------------------- |
------------------------------------------------------------- |
| `../public-api.ts` | Public types exported to plugin authors |
| `../loaded-nx-plugin.ts` | Interface definition for loaded plugins |
| `messaging.ts` | Message type definitions for worker communication |
| `plugin-worker.ts` | Worker process - receives messages, calls plugin
functions |
| `isolated-plugin.ts` | Main class - spawns worker, sends messages,
manages lifecycle |
| `plugin-lifecycle-manager.ts` | Tracks phases, decides when to
shutdown |
| `load-isolated-plugin.ts` | Caching layer for isolated plugins |
| `../get-plugins.ts` | Entry point - decides isolation mode |

## Lifecycle Phases

```
LOADED → [graph] → [pre-task] → {tasks run} → [post-task]
           │           │                           │
           │           └── preTasksExecution ──────┤
           │                                       │
           ├── createNodes                         │
           ├── createDependencies                  │
           └── createMetadata                      │
                                                   │
                                    postTasksExecution
```

**Shutdown rules:**

- Plugin shuts down after its last active phase completes
- If only `postTasksExecution`: shutdown immediately after load, restart
when needed
- Concurrent callers tracked via session count (ref counting)

---------

Co-authored-by: nx-cloud[bot] <71083854+nx-cloud[bot]@users.noreply.github.com>
Co-authored-by: AgentEnder <AgentEnder@users.noreply.github.com>
2026-02-06 14:54:10 -05:00
Caleb Ukle 359c7fbf4e fix(nx-dev): include nx cli examples on refs page (#34367)
![wm_2026-02-06T11-26-54@2x](https://github.com/user-attachments/assets/c2814268-e921-420b-a9e2-2b90c8a89526)

https://deploy-preview-34367--nx-docs.netlify.app/docs/reference/nx-commands#nx-add

cli examples are included in the generated page now

fixes: DOC-402
2026-02-06 12:39:00 -05:00
James Henry 0d2882e1c0 fix(core): use picocolors instead of chalk in the nx package (#34305) 2026-02-06 19:17:52 +04:00
Colum Ferry 541498f58a feat(core): update cnw messaging (#34364)
CLOSES CLOUD-4235
2026-02-06 10:04:42 -05:00
Colum Ferry b85ac155cf feat(js): update swc/cli to 0.8.0 (#34365)
Update `@swc/cli` to 0.8.0 which uses chokidar v5
2026-02-06 08:47:56 -05:00
MaxKless 5f42ccc3e9 docs(maven): minor tweaks and include targetNamePrefix (#34362)
this makes the maven documentation more complete for the latest changes
2026-02-06 13:30:44 +00:00
Louie Weng 8fdfc523a8 chore(gradle): bump gradle version to 0.1.12 (#34250)
<!-- Please make sure you have read the submission guidelines before
posting an PR -->
<!--
https://github.com/nrwl/nx/blob/master/CONTRIBUTING.md#-submitting-a-pr
-->

<!-- Please make sure that your commit message follows our format -->
<!-- Example: `fix(nx): must begin with lowercase` -->

<!-- If this is a particularly complex change or feature addition, you
can request a dedicated Nx release for this pull request branch. Mention
someone from the Nx team or the `@nrwl/nx-pipelines-reviewers` and they
will confirm if the PR warrants its own release for testing purposes,
and generate it for you if appropriate. -->

## Current Behavior
<!-- This is the behavior we have today -->

## Expected Behavior
<!-- This is the behavior we should expect with the changes in this PR
-->

Gradle plugin to 0.1.12

## Related Issue(s)
<!-- Please link the issue being fixed so it gets closed when this is
merged. -->

Fixes #

---------

Co-authored-by: nx-cloud[bot] <71083854+nx-cloud[bot]@users.noreply.github.com>
2026-02-05 15:32:01 -08:00
Louie Weng a44a27b29a feat(gradle): add debug env var to gradle batch executor (#34259)
<!-- Please make sure you have read the submission guidelines before
posting an PR -->
<!--
https://github.com/nrwl/nx/blob/master/CONTRIBUTING.md#-submitting-a-pr
-->

<!-- Please make sure that your commit message follows our format -->
<!-- Example: `fix(nx): must begin with lowercase` -->

<!-- If this is a particularly complex change or feature addition, you
can request a dedicated Nx release for this pull request branch. Mention
someone from the Nx team or the `@nrwl/nx-pipelines-reviewers` and they
will confirm if the PR warrants its own release for testing purposes,
and generate it for you if appropriate. -->

## Current Behavior
<!-- This is the behavior we have today -->

- No way to run the batch executor in debug mode
- Any flags passed into the nx gradle batch command get forwarded into
the `gradlew` command.

## Expected Behavior
<!-- This is the behavior we should expect with the changes in this PR
-->

- Allow for an env variable to be set for debug flags so that the batch
runner jar can be run with a debugger hooked in.

## Related Issue(s)
<!-- Please link the issue being fixed so it gets closed when this is
merged. -->

Fixes #NXC-3797

---------

Co-authored-by: Jason Jean <jasonjean1993@gmail.com>
2026-02-05 22:26:02 +00:00
Leosvel Pérez Espinosa 0cac182b73 fix(core): avoid crash when pane area is out of bounds during resize (#34343)
## Current Behavior

In some resize/background scenarios, the TUI could crash while rendering
output panes, displaying a "Scrollbar area is empty" message.

## Expected Behavior

The TUI remains stable during resizes and backgrounding. Output panes no
longer crash when a scrollbar would render in an invalid area.
2026-02-05 17:11:47 -05:00
Louie Weng b69ff4ceb9 chore(repo): fix broken disablement command (#34355)
<!-- Please make sure you have read the submission guidelines before
posting an PR -->
<!--
https://github.com/nrwl/nx/blob/master/CONTRIBUTING.md#-submitting-a-pr
-->

<!-- Please make sure that your commit message follows our format -->
<!-- Example: `fix(nx): must begin with lowercase` -->

<!-- If this is a particularly complex change or feature addition, you
can request a dedicated Nx release for this pull request branch. Mention
someone from the Nx team or the `@nrwl/nx-pipelines-reviewers` and they
will confirm if the PR warrants its own release for testing purposes,
and generate it for you if appropriate. -->

## Current Behavior
<!-- This is the behavior we have today -->

## Expected Behavior
<!-- This is the behavior we should expect with the changes in this PR
-->

## Related Issue(s)
<!-- Please link the issue being fixed so it gets closed when this is
merged. -->

Fixes #
2026-02-05 17:11:01 -05:00
Louie Weng 4337d2e928 fix(gradle): use gradle project name when resolving dependent tasks (#34331)
<!-- Please make sure you have read the submission guidelines before
posting an PR -->
<!--
https://github.com/nrwl/nx/blob/master/CONTRIBUTING.md#-submitting-a-pr
-->

<!-- Please make sure that your commit message follows our format -->
<!-- Example: `fix(nx): must begin with lowercase` -->

<!-- If this is a particularly complex change or feature addition, you
can request a dedicated Nx release for this pull request branch. Mention
someone from the Nx team or the `@nrwl/nx-pipelines-reviewers` and they
will confirm if the PR warrants its own release for testing purposes,
and generate it for you if appropriate. -->

## Current Behavior
<!-- This is the behavior we have today -->

When Nx processes Gradle subprojects, task dependencies reference the
wrong project names. For a subproject structure like :app or :lib:core,
the generated task dependencies use only the simple project name (app or
core) instead of the full build tree path (:app or :lib:core). This
causes dependent tasks to be generated with incorrect project
references, breaking the task graph for multi-project Gradle builds.

## Expected Behavior
Task dependencies should use the full Gradle build tree path for
subprojects. When a task in :app depends on a task in :lib, the
dependency should be correctly referenced as :lib:taskName.

The fix introduces a getNxProjectName() utility function that correctly
resolves the Nx project name based on the Gradle project's
buildTreePath, and applies it consistently across all dependency
resolution logic in ProjectUtils.kt and
TaskUtils.kt. New tests verify the fix works for both single and nested
subproject structures.

Also removed --rerun-tasks from the batch and non batch runners, we
found that during parallel task executions with the non batch runner,
the flag would cause cache conflicts that would fail tasks.

## Related Issue(s)
<!-- Please link the issue being fixed so it gets closed when this is
merged. -->

Fixes #

---------

Co-authored-by: nx-cloud[bot] <71083854+nx-cloud[bot]@users.noreply.github.com>
2026-02-05 13:44:05 -08:00
Leosvel Pérez Espinosa a0be6adcf8 fix(core): track all task outputs regardless of path depth (#34321)
## Current Behavior

When a task has outputs at different path depths, some outputs may not
be tracked. This causes:

- Deleted output files not being detected
- Cache restoration being skipped with message "existing outputs match
the cache, left as is"
- Files not being restored even though they exist in cache

## Expected Behavior

All task outputs are tracked regardless of their path depth, ensuring:

- Deleted outputs are correctly detected
- Cache restoration happens when outputs are missing
2026-02-05 16:41:39 -05:00
Leosvel Pérez Espinosa dffdfa694c fix(core): disable ignore filters for outputs expansion (#34316)
## Current Behavior

When a task output directory contains a nested `.gitignore` that hides
its contents, Nx can treat the outputs as already present and skip
restoring them from cache. This can result in generated files being
missing from disk, even though the cache entry is valid.

## Expected Behavior

Nx should restore cached outputs regardless of ignore rules inside the
output directory.

## Related Issue(s)

Fixes #32620
2026-02-05 16:37:14 -05:00
Louie Weng a40ff52db0 chore(gradle): temporarily disable e2e tests (#34351)
<!-- Please make sure you have read the submission guidelines before
posting an PR -->
<!--
https://github.com/nrwl/nx/blob/master/CONTRIBUTING.md#-submitting-a-pr
-->

<!-- Please make sure that your commit message follows our format -->
<!-- Example: `fix(nx): must begin with lowercase` -->

<!-- If this is a particularly complex change or feature addition, you
can request a dedicated Nx release for this pull request branch. Mention
someone from the Nx team or the `@nrwl/nx-pipelines-reviewers` and they
will confirm if the PR warrants its own release for testing purposes,
and generate it for you if appropriate. -->

## Current Behavior
<!-- This is the behavior we have today -->

## Expected Behavior
<!-- This is the behavior we should expect with the changes in this PR
-->

Disabling Gradle e2e tests until foojay toolchain service back online.

Ensures that gradle within the Nx repo uses mise to download java
toolchain, but gradle workspaces within the e2e environments download
their own toolchain.

## Related Issue(s)
<!-- Please link the issue being fixed so it gets closed when this is
merged. -->

Fixes #Q-175
2026-02-05 16:32:59 -05:00
Philip Fulcher bd41ce5692 docs(nx-dev): add feb 2026 webinar (#34352) 2026-02-05 16:24:18 -05:00
Philip Fulcher 1f09a5d1a5 docs(nx-dev): remove errant author from article (#34349) 2026-02-05 14:58:04 -05:00
Jack Hsu 0b99f560d4 feat(core): add AI agent detection and NDJSON output for CNW (#34320)
AI agents are detected via environment variables (CLAUDECODE, OPENCODE)
and receive NDJSON streaming output, non-interactive mode, structured
JSON results with explicit GitHub setup instructions.

Related NXC-3628

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-05 14:57:39 -05:00
Caleb Ukle 866d5b1e9b fix(nx-dev): use right URL for the given netlify context (#34348) 2026-02-05 13:19:17 -05:00
Caleb Ukle d4ad62f522 fix(nx-dev): fix og images wrong URL for embeds (#34346)
fixes: DOC-399

next used the VERCEL_URL by default for metadataBase. this is not
present in netlify. so resolve to netlify URLs if VERCEL_URL is not
present so metadata links are correct.

working on PR:
<img width="704" height="875" alt="image"
src="https://github.com/user-attachments/assets/4edd194e-27d6-46ce-bdd2-7da4ab70d482"
/>
<img width="976" height="204" alt="image"
src="https://github.com/user-attachments/assets/63ea32f5-20cf-4df2-96a1-a1bc2ff59411"
/>



https://6984ce9d1bad30000873247d--nx-dev.netlify.app/blog/nx-2026-roadmap
2026-02-05 17:45:12 +00:00
Louie Weng 3aff575002 docs(gradle): add reference to batch mode (#34271)
<!-- Please make sure you have read the submission guidelines before
posting an PR -->
<!--
https://github.com/nrwl/nx/blob/master/CONTRIBUTING.md#-submitting-a-pr
-->

<!-- Please make sure that your commit message follows our format -->
<!-- Example: `fix(nx): must begin with lowercase` -->

<!-- If this is a particularly complex change or feature addition, you
can request a dedicated Nx release for this pull request branch. Mention
someone from the Nx team or the `@nrwl/nx-pipelines-reviewers` and they
will confirm if the PR warrants its own release for testing purposes,
and generate it for you if appropriate. -->

## Current Behavior
<!-- This is the behavior we have today -->

## Expected Behavior
<!-- This is the behavior we should expect with the changes in this PR
-->

Add documentation for batch mode. Remove references to removed custom
overrides for intTest.

## Related Issue(s)
<!-- Please link the issue being fixed so it gets closed when this is
merged. -->

Fixes #
2026-02-05 08:07:05 -08:00
Jonathan Cammisuli 7df9d96dac feat(core): add command to download cloud client (#34333)
<!-- Please make sure you have read the submission guidelines before
posting an PR -->
<!--
https://github.com/nrwl/nx/blob/master/CONTRIBUTING.md#-submitting-a-pr
-->

<!-- Please make sure that your commit message follows our format -->
<!-- Example: `fix(nx): must begin with lowercase` -->

<!-- If this is a particularly complex change or feature addition, you
can request a dedicated Nx release for this pull request branch. Mention
someone from the Nx team or the `@nrwl/nx-pipelines-reviewers` and they
will confirm if the PR warrants its own release for testing purposes,
and generate it for you if appropriate. -->

## Current Behavior
<!-- This is the behavior we have today -->
The only way to download the cloud client is to run a specific cloud
command or a task with cloud configured.

## Expected Behavior
<!-- This is the behavior we should expect with the changes in this PR
-->
A new command is added where we only download the cloud client with:
```
nx download-cloud-client
```

## Related Issue(s)
<!-- Please link the issue being fixed so it gets closed when this is
merged. -->

Fixes #

---------

Co-authored-by: nx-cloud[bot] <71083854+nx-cloud[bot]@users.noreply.github.com>
2026-02-05 10:42:55 -05:00
Caleb Ukle 05fb208fda docs(node): add bundling guide (#34244)
add guide to clarify various ways to bundle a node app for different
bundlers

---------

Co-authored-by: nx-cloud[bot] <71083854+nx-cloud[bot]@users.noreply.github.com>
Co-authored-by: barbados-clemens <barbados-clemens@users.noreply.github.com>
Co-authored-by: graphite-app[bot] <96075541+graphite-app[bot]@users.noreply.github.com>
2026-02-05 15:25:42 +00:00
Philip Fulcher 33f031be04 docs(nx-dev): add 2026 roadmap article (#34327) 2026-02-05 08:47:23 -06:00
James Henry 0e11f95163 chore(repo): add dependabot config to try and remove false positives (#34341) 2026-02-05 18:26:46 +04:00
Juri Strumpflohner 2b07ac22c2 feat(core): improve AI agent rules for CLAUDE.md generation (#34304)
## Summary

Updates the generated CLAUDE.md content with improved guidance for AI
agents working with Nx workspaces.

**Changes:**
- Add "nx-workspace skill first" for workspace navigation
- Add "prefix nx commands with package manager" rule
- Add "NEVER guess CLI flags" rule
- Add "Scaffolding & Generators" section (invoke nx-generate skill
first)
- Add "When to use nx_docs" guidance (USE for advanced config, DON'T USE
for basic syntax)

## Why These Changes

Based on testing with repeated scaffolding tasks, agents were:

| Issue | Fix |
|-------|-----|
| Calling `nx_docs` for basic generator syntax | Added clear guidance on
when to use/not use nx_docs |
| Guessing CLI flags incorrectly (e.g., `nx sync --apply`) | Added
"never guess flags" rule |
| Using global nx CLI causing version mismatches | Added package manager
prefix rule |
| Not invoking nx-generate skill on scaffolding tasks | Added explicit
"Scaffolding & Generators" section |

## Related

Companion PR with skill improvements:
https://github.com/nrwl/nx-ai-agents-config/pull/26

---------

Co-authored-by: nx-cloud[bot] <71083854+nx-cloud[bot]@users.noreply.github.com>
2026-02-05 21:17:59 +09:00
Jason Jean efa364f73d fix(core): preserve task selection when unrelated tasks finish (#34328)
## Current Behavior

In the TUI, when any standalone task finishes,
`handle_standalone_task_finished` unconditionally switches the user's
selection to another in-progress task — even if the finished task wasn't
the one the user had selected. This causes the selection to jump
unexpectedly while the user is watching a different task.

## Expected Behavior

Selection should only change when the task the user is actively viewing
finishes. If an unrelated background task finishes, the user's selection
should remain on whatever they chose.

## Related Issue(s)

N/A — discovered during development testing.

---------

Co-authored-by: nx-cloud[bot] <71083854+nx-cloud[bot]@users.noreply.github.com>
Co-authored-by: FrozenPandaz <FrozenPandaz@users.noreply.github.com>
2026-02-04 21:23:22 -05:00
Craigory Coppola 82265ff157 fix(core): allow overriding daemon logging settings (#34324)
## Current Behavior
NX_NATIVE_LOGGING is hardcoded and can't be customized on the daemon
server

## Expected Behavior
Log settings can be customized by changing them in the env of the first
command to spawn the daemon

## Related Issue(s)
<!-- Please link the issue being fixed so it gets closed when this is
merged. -->

Fixes #
2026-02-04 21:12:23 -05:00
Jason Jean 015ff1a45b chore(repo): update nx to 22.5.0-beta.4 (#34334)
Updating Nx from 22.5.0-beta.3 to 22.5.0-beta.4
2026-02-05 00:27:21 +00:00
Caleb Ukle cdb4bb2acc fix(nx-dev): exclude large native deps from build bundle (#34335)
we were including native binaries in the final netlify function build
for nextjs which was fine until we reach the limit of 250mb causing a
failure to upload the function (AWS imposed lambda limit)

Now we strip out any deps we know we don't need for the app which are
dev deps and not runtime required.
2026-02-04 19:09:25 -05:00
Jason Jean 3c7f94e3d5 chore(repo): align canary and PR release versions with next (#34330)
## Current Behavior

- Canary releases calculate their base version by incrementing the minor
of `nx@latest`, or using `nx@next` major when majors differ
- PR releases always use `0.0.0` as their base version (e.g.
`0.0.0-pr-1234-abc1234`)
- This means canary and PR versions don't clearly relate to the current
beta release line

## Expected Behavior

- Both canary and PR releases derive their base version directly from
`nx@next`
- If next is `22.5.0-beta.5`, then:
  - Canary: `22.5.0-canary.20260204-abc1234`
  - PR: `22.5.0-pr.1234.abc1234`
- All prerelease channels now share the same base version, making it
clear which release line they belong to

## Related Issue(s)

N/A - internal improvement to release infrastructure
2026-02-04 16:36:44 -05:00
Jason Jean ef55f97df3 feat(maven): bump maven plugin version to 0.0.13 (#34318)
## Current Behavior

The Maven plugin version is `0.0.12` across all pom.xml files and the
versions.ts constant. The `bump-maven-version` generator does not update
the `batch-runner-adapters` pom files, causing version mismatches.

## Expected Behavior

The Maven plugin version is bumped to `0.0.13` in **all** pom.xml files
(including batch-runner-adapters), with a migration created for users
upgrading to Nx `22.5.0-beta.4`. The bump generator now includes the
batch-runner-adapters pom files so future bumps won't miss them.

### Changes
- Updated version from `0.0.12` to `0.0.13` in all pom.xml files (root,
maven, maven-plugin, shared, batch-runner, batch-runner-adapters,
maven3-adapter, maven4-adapter)
- Updated `mavenPluginVersion` constant in
`packages/maven/src/utils/versions.ts`
- Added `update-0-0-13` migration entry in
`packages/maven/migrations.json` targeting Nx `22.5.0-beta.4`
- Created migration file
`packages/maven/src/migrations/0-0-13/update-pom-xml-version.ts`
- Fixed `bump-maven-version` generator to include
`batch-runner-adapters` pom files
2026-02-04 14:06:18 -05:00
Jack Hsu 5880551c6a chore(repo): update docs readme with guiding principles (#34319)
This PR adds info for how we structure the docs so when we write them we
know where to put things, what to write, etc.
2026-02-04 13:40:08 -05:00
Jason Jean fb6c2982e6 fix(misc): improve freebsd build reliability with better error handling and disk cleanup (#34326)
## Current Behavior

The FreeBSD build in CI can fail silently or with unclear error messages
when:
- Disk space runs low during the build process
- The build command fails without proper error propagation
- Unnecessary files consume valuable disk space

## Expected Behavior

With these changes:
- Additional disk space is freed by removing docs/astro-docs/nx-dev
directories before building
- Build exit codes are properly captured and propagated
- Disk usage is logged after the build completes for debugging purposes
- Build failures are clearly reported with explicit error messages

This improves reliability and makes it easier to diagnose issues when
they occur.

## Related Issue(s)

<!-- No specific issue, general CI improvement -->
2026-02-04 13:34:52 -05:00
Caleb Ukle 81944b02c5 feat(nx-dev): reformat sidebar into topics (#34265)
Sidebar contains everything about the docs which is overhelming and hard
to find information a person could be looking for.

instead we move into "topics" to section the sidebar based on intention
of content, making it easier to have a journey through the docs or jump
straight to the content someone could be looking for.

This doesn't change any routes of pages, just hard links into the
sidebar (instead of autogenerate based on dir) in the future once we
solidify where everything will live, we can come back and rearrange
files to reuse the autogenerate dirs.


![wm_2026-02-04T10-48-54](https://github.com/user-attachments/assets/6add05b3-b929-492f-a54e-fde6c2929973)

![wm_2026-02-04T10-50-10](https://github.com/user-attachments/assets/e9c89621-c287-4c9a-8d14-692bd1a73739)
<img width="1385" height="1003" alt="image"
src="https://github.com/user-attachments/assets/4bbee4fe-2f7e-4d7a-ad6b-45f6064c897a"
/>

![wm_2026-02-04T11-03-15@2x](https://github.com/user-attachments/assets/10561621-5c80-4843-a7c4-a1272adf7b8e)

---------

Co-authored-by: nx-cloud[bot] <71083854+nx-cloud[bot]@users.noreply.github.com>
Co-authored-by: barbados-clemens <barbados-clemens@users.noreply.github.com>
2026-02-04 11:57:47 -06:00
Jack Hsu c1cc626e52 docs(misc): filter out non-existing image requests and also only track HTML page views when requested with "Accept: text/html" (#34317)
We're getting requests to `favicon.svg.md` that are being tracked,
ignore these. Also for the server page views, we should only count them
if `text/html` is in the accept header. Browsers will send these, and AI
agents, curl, etc. do not. This allows us to compare browser traffic vs
AI/curl traffic more accurately.
2026-02-04 10:38:25 -05:00
Jason Jean 6f3d38ad3c fix(core): handle EPIPE errors gracefully in daemon socket writes (#34311)
## Current Behavior

When a client disconnects while the daemon is writing a response, a
`socket.write` call triggers an EPIPE error. The old error handler used
`console.error`, which caused the error to propagate through
`respondWithErrorAndExit` and crash the daemon process via
`process.exit(1)`. The client would then see an `internalDaemonError`
and permanently disable the daemon via `markDaemonAsDisabled`, requiring
`nx reset` to recover.

Additionally, disconnected sockets were not cleaned up from the file
watcher and project graph listener registries on socket error events,
only on `close` events. This left a window where the daemon could
attempt to write to dead sockets during notifications.

## Expected Behavior

When a client disconnects mid-response:
- The `socket.write` callback logs the error gracefully via
`serverLogger` instead of `console.error`
- The daemon process stays alive and continues serving other clients
- The `socket.on('error')` handler cleans up registered file watcher and
project graph listener sockets immediately, matching the existing
`close` handler behavior
- The daemon is never permanently disabled due to EPIPE errors

## Related Issue(s)

<!-- No linked issue -->
2026-02-04 09:43:59 -05:00
MaxKless d0e4a92738 fix(core): tweak configure-ai-agents messaging (#34307)
### Current Behavior
The nx configure-ai-agents command output is minimal - just "AI agents
set up successfully" with a
bullet list of agent names. Users don't understand what was actually
configured (plugin vs MCP,
skills, which files were created/modified).
### Expected Behavior
Clearer feedback about what gets configured for each agent:
Selection prompt improvements:
- Agents needing updates show (update available) tag
- Footer always shows result state: what will be configured
- Agent-specific descriptions (e.g., "Installs Nx plugin (MCP + skills +
agents). Updates
CLAUDE.md.")
Post-configuration output:
- Compact summary per agent showing what was set up
- Example: Claude Code: Nx plugin (MCP + skills + agents) + CLAUDE.md
Claude .mcp.json cleanup:
- When configuring Claude, removes nx-mcp from .mcp.json since it's now
handled by the plugin
- Deletes the file entirely if nx-mcp was the only entry

---------

Co-authored-by: nx-cloud[bot] <71083854+nx-cloud[bot]@users.noreply.github.com>
2026-02-04 09:43:38 -05:00
Jack Hsu 2ea6961e0d fix(core): fix CNW git amend and README marker handling (#34306)
This PR fixes two issues:
1. When the README changes are amended, there's an edge case where we
don't have a commit to amend (e.g. `--skipGit`), and this fails the
entire CNW flow.
2. When user opts out of Cloud, we strip the entire `<!-- BEGIN:
nx-cloud -->` block in README rather than just the comments and leaving
the content.

Closes NXC-3812

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 08:50:42 -05:00
Benjamin Cabanes 1195565b96 docs(nx-dev): update Nx Cloud & Home features, add new components (#34149) 2026-02-03 18:04:01 -05:00
Craigory Coppola 7478cbb59d feat(core): add initial impl of task io service (#34205)
## Current Behavior
There's not an easy to use service to track PIDs being registered to nx
tasks

## Expected Behavior
There's a service to track this stuff

## Related Issue(s)
2026-02-03 17:42:17 -05:00
Jason Jean 79d878f240 fix(core): prevent command injection in getNpmPackageVersion (#34309)
## Current Behavior

The `getNpmPackageVersion` function in
`packages/workspace/src/generators/utils/get-npm-package-version.ts`
uses `execSync` with direct string interpolation of the `packageName`
parameter. When a user runs `create-nx-workspace` with a custom
`--preset` value that doesn't match a known preset, the value flows
unsanitized into a shell command:

```js
execSync(`npm view ${packageName}... version --json`)
```

This allows arbitrary command execution via shell metacharacters (e.g.,
`--preset='pkg$(malicious command)'`).

## Expected Behavior

User-supplied package names are validated against a strict npm package
name regex before being passed to any shell command. The function now
uses `execFileSync` with an args array instead of `execSync` with string
interpolation, providing defense in depth:

1. **Input validation** — rejects anything that isn't a valid npm
package name
2. **Safe execution** — arguments are passed as an array so Node.js
handles escaping, rather than concatenating into a raw shell string
2026-02-03 16:18:43 -05:00
Caleb Ukle b198606bef docs(nx-cloud): add screenshots for cache troubleshoot guide (#34296) 2026-02-03 15:35:34 -05:00
Craigory Coppola 1cb6c0b14c fix(core): nx should show help for run-one when using project short names (#34303)
## Current Behavior
Given a project name like `:foo`, you can run tasks like `nx test foo`
(note `foo` vs `:foo`), but passing --help throws an error

## Expected Behavior
`--help` works the same with the shortname vs full name

## Related Issue(s)
<!-- Please link the issue being fixed so it gets closed when this is
merged. -->

Fixes #

---------

Co-authored-by: nx-cloud[bot] <71083854+nx-cloud[bot]@users.noreply.github.com>
2026-02-03 14:56:23 -05:00
Jason Jean 3f6bdc7ff7 fix(maven): include pom.xml and ancestor pom files as inputs for all targets (#34291)
## Current Behavior

- `pom.xml` is only included as an input when a mojo uses default inputs
- Mojos with specific input configurations (like
`maven-compiler-plugin:compile`) don't get `pom.xml` in their inputs
- Parent `pom.xml` files aren't tracked as inputs

This can lead to stale cache hits when:
1. `pom.xml` changes but a mojo has specific input config
2. A parent `pom.xml` changes (affecting inherited properties,
dependency versions, plugin config)

## Expected Behavior

- Every target should include its own `pom.xml` as an input
- Every target should include ancestor `pom.xml` files (within the
workspace) as inputs
- Cache should invalidate when any relevant `pom.xml` changes

## Related Issue(s)

N/A - discovered during code review

## Changes

- **CacheConfig.kt**: Removed `pom.xml` from `defaultInputs` (now always
added explicitly)
- **MojoAnalyzer.kt**: Added `workspaceRoot` parameter and logic to walk
up the parent chain, adding all in-workspace ancestor `pom.xml` files as
inputs
- **NxProjectAnalyzerMojo.kt**: Pass `workspaceRoot` to `MojoAnalyzer`
2026-02-03 14:26:11 -05:00
Jason Jean cc4ec68bce chore(repo): update nx to 22.5.0-beta.3 (#34295)
Updating Nx from 22.5.0-beta.2 to 22.5.0-beta.3
2026-02-03 14:20:30 -05:00
Juri 01d2f64b90 docs(nx-dev): add autonomous AI workflows blog post
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-03 17:26:28 +01:00
James Henry 39d8a9a6ac chore(repo): update to pnpm@10.28.2 and clean up pnpm config (#34298) 2026-02-03 17:48:01 +04:00
Jack Hsu 4f02c6b56e docs(misc): ignore /docs/og/*.png.md paths (#34289)
This PR excludes `/docs/og/*` paths from assets tracking. This is likely
added by some crawler and is additional compute/noise that we don't care
about.

<img width="1354" height="86" alt="image"
src="https://github.com/user-attachments/assets/6bfda816-0d26-46b3-b432-ae96e5976c37"
/>
2026-02-02 13:32:06 -05:00
Jack Hsu 3f77cd5927 fix(nx-dev): fix double-counting and exclude assets from page tracking (#34286)
## Current Behavior

1. **track-asset-requests** runs twice per request due to redundant path
patterns:
   ```typescript
   path: ["/*.txt", "/**/*.txt", "/*.md", "/**/*.md"]
   ```
The `/**/*` pattern already matches root level files, so `/*` is
redundant.

2. **track-page-requests** runs on many asset requests even though it
only tracks HTML page views:
   - Font files: `/docs/fonts/*.woff2`, `/docs/*.woff`
   - Images: `/docs/*.svg`, `/docs/*.png`, `/docs/og/*`
   - Pagefind search index: `/docs/pagefind/*`

## Expected Behavior

1. Asset tracking should fire only once per request
2. Page tracking should exclude all non-HTML assets at Netlify level
(zero compute)

## Changes

### track-asset-requests.ts
Simplified path patterns:
```typescript
path: ["/**/*.txt", "/**/*.md"]
```

### track-page-requests.ts
Added comprehensive exclusions:

| Category | Exclusions |
|----------|------------|
| Images | `.svg`, `.png`, `.jpg`, `.jpeg`, `.gif`, `.webp`, `.ico`,
`/images/*`, `/og/*` |
| Fonts | `/fonts/*`, `.woff`, `.woff2` |
| Search | `/pagefind/*` |

## Related Issue(s)

Fixes DOC-395
2026-02-02 12:17:32 -05:00
Jason Jean 89aa25e5d0 fix(core): resolve daemon client reconnect queue deadlock (#34284)
## Current Behavior

When the daemon dies while processing a request, the reconnect logic
adds the retry back to the promise-based queue. However, the original
request is still blocked in the queue waiting for a response that will
never come (the socket is dead). This creates a deadlock:

1. Original request (`fn1`) is blocked awaiting a promise that will
never resolve
2. Retry request (`fn2`) is queued but can't execute until `fn1`
completes
3. `fn1` can't complete because it's waiting on the dead socket

## Expected Behavior

When reconnecting after daemon death, the retry should resolve the
pending promise that the original queue entry is waiting on, allowing
the queue to proceed normally.

## Related Issue(s)

<!-- No specific issue, discovered during development -->

## Solution

Instead of re-queuing the retry through `sendToDaemonViaQueue` (which
adds to the end of the queue), we now call `sendMessageToDaemon`
directly. This resolves the pending promise that `fn1` is waiting on,
allowing it to complete naturally and the queue to proceed.

Also removed the now-unused `decrementQueueCounter` method from
`PromisedBasedQueue`.
2026-02-02 12:05:33 -05:00
Jack Hsu cdd735dc63 feat(nx-dev): add server-side page view tracking for docs (#34283)
## Current Behavior

Only markdown and text file requests are tracked server-side via the
`track-asset-requests` edge function. HTML page views are not tracked on
the server, missing requests from AI tools and curl.

## Expected Behavior

Track all doc page views server-side with a new edge function that:
- Sends `server_page_view` events to GA4 with `content_type` param to
differentiate HTML/markdown/text
- Uses Netlify's `excludedPath` config for efficient path filtering
(zero compute for excluded paths)
- Skips non-HTML requests via Accept header check

### Changes

| File | Change |
|------|--------|
| `track-page-requests.ts` | **NEW** - Edge function for HTML page view
tracking on `/docs/*` |
| `track-asset-requests.ts` | Changed event name to `server_page_view`,
added `content_type` param |
| `add-link-headers.ts` | Refactored to use `excludedPath` config
instead of runtime path checks |
| `netlify.toml` | Added edge function declaration for
`track-page-requests` |

### GA Event Schema

```javascript
{
  name: 'server_page_view',
  params: {
    content_type: 'text/html' | 'text/markdown' | 'text/plain',
    file_extension: '.html' | '.md' | '.txt',
    is_ai_tool: 'true' | 'false',
    // ... other params
  }
}
```

## Other Notes

This PR also removes the edge function entries from `netlify.toml` since
it's auto detected from `astro-docs/netlify/edge-functions`. This makes
all the configuration in the actual `.ts` file, not duplicated in the
`netlify.toml` file.

## Related Issue(s)

Closes DOC-395

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 11:15:01 -05:00
Juri Strumpflohner f59c15a410 docs(core): update AI pages and include new info about configure-ai-agents command (#34257)
changes to:
-
https://deploy-preview-34257--nx-docs.netlify.app/docs/getting-started/ai-setup
-
https://deploy-preview-34257--nx-docs.netlify.app/docs/features/enhance-ai
-
https://deploy-preview-34257--nx-docs.netlify.app/docs/reference/nx-mcp

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: graphite-app[bot] <96075541+graphite-app[bot]@users.noreply.github.com>
2026-02-02 15:58:32 +00:00
Louie Weng 94319c7531 fix(gradle): enforce that only one gradle task can be passed into gradle executor (#34269)
<!-- Please make sure you have read the submission guidelines before
posting an PR -->
<!--
https://github.com/nrwl/nx/blob/master/CONTRIBUTING.md#-submitting-a-pr
-->

<!-- Please make sure that your commit message follows our format -->
<!-- Example: `fix(nx): must begin with lowercase` -->

<!-- If this is a particularly complex change or feature addition, you
can request a dedicated Nx release for this pull request branch. Mention
someone from the Nx team or the `@nrwl/nx-pipelines-reviewers` and they
will confirm if the PR warrants its own release for testing purposes,
and generate it for you if appropriate. -->

## Current Behavior
<!-- This is the behavior we have today -->

The Gradle executor accepts a taskName option that *should not* contain
multiple space-separated tasks. When multiple tasks are provided, the
batch runner misinterprets the space-separated string as containing
project names rather than treating it as a single task argument, leading
to execution errors and confusion.

This only occurs if the taskName is manually overridden and should not
occur when task names are generated by the project graph plugin.

## Expected Behavior
<!-- This is the behavior we should expect with the changes in this PR
-->

The Gradle executor now validates that taskName contains only a single
task without spaces. If multiple tasks are passed, it throws a clear
error message: "Task '[taskName]' contains spaces. Only a single Gradle
task is allowed per executor invocation." This prevents the batch runner
from misinterpreting the task name and provides immediate feedback to
users about the correct usage.

## Related Issue(s)
<!-- Please link the issue being fixed so it gets closed when this is
merged. -->

Fixes #
2026-02-02 10:14:08 -05:00
Louie Weng 35bc17e4fe fix(gradle): ensure that batch output is not overriden for atomized targets (#34268)
<!-- Please make sure you have read the submission guidelines before
posting an PR -->
<!--
https://github.com/nrwl/nx/blob/master/CONTRIBUTING.md#-submitting-a-pr
-->

<!-- Please make sure that your commit message follows our format -->
<!-- Example: `fix(nx): must begin with lowercase` -->

<!-- If this is a particularly complex change or feature addition, you
can request a dedicated Nx release for this pull request branch. Mention
someone from the Nx team or the `@nrwl/nx-pipelines-reviewers` and they
will confirm if the PR warrants its own release for testing purposes,
and generate it for you if appropriate. -->

## Current Behavior

When running Gradle tasks in batch mode with atomized targets, if the
same task appears multiple times in the output (which happens when tasks
are atomized and executed separately), the batch runner only captures
the output from the last execution. Previous executions' output gets
overwritten because the splitOutputPerTask function replaces the entire
output for each task name it encounters.

## Expected Behavior

All output from a task should be preserved, even when the task appears
multiple times in the batch output. When the same task name is
encountered multiple times, the outputs should be concatenated rather
than replaced, ensuring developers can see the complete execution
history for atomized targets.



## Related Issue(s)
<!-- Please link the issue being fixed so it gets closed when this is
merged. -->

Fixes #
2026-02-02 10:12:27 -05:00
Copilot d65dcfa806 docs(dotnet): fix build target dependsOn example (#34206)
## Plan for fixing .NET incremental builds documentation

- [x] Update the "Target dependencies" section to remove `restore` from
the `dependsOn` array
- [x] Add explanation about why `restore` cannot be run through Nx for
solutions using custom frameworks
- [x] Improve explanation clarity based on review feedback
- [x] Verify the "Target configuration" section is consistent with the
changes
- [x] Complete code review and address feedback
- [x] Run security checks (no issues found)
- [x] Address PR feedback: Use JSX `<Aside>` component with import
statement instead of Markdoc tag

## Summary

Successfully fixed the documentation issue in the .NET incremental
builds guide. The changes made:

1. **Removed `restore` from the build target's `dependsOn` array** - The
documentation now correctly shows `"dependsOn": ["^build"]` instead of
`"dependsOn": ["restore", "^build"]`, matching the actual implementation
in the plugin code.

2. **Added a clear explanation** - Included an aside box explaining why
`restore` is not in the `dependsOn` array: because Nx requires NuGet
package restoration to be completed before running any tasks, and
including it would create a circular dependency.

3. **Verified consistency** - Checked that the "Target configuration"
section already showed the correct configuration, ensuring all
documentation is now consistent.

4. **Used correct Starlight component** - Changed from Markdoc `{% aside
%}` tag to JSX `<Aside>` component with proper import statement per
Starlight documentation standards.

The changes align with the actual implementation in
`packages/dotnet/analyzer/Utilities/TargetBuilder.Build.cs` where the
build target's `dependsOn` is set to `[$"^{targetName}"]` (line 56).

<!-- START COPILOT ORIGINAL PROMPT -->



<details>

<summary>Original prompt</summary>

> 
> ----
> 
> *This section details on the original issue you should resolve*
> 
> <issue_title>docs(dotnet): implied conflict in dependsOn of inferred
build task</issue_title>
> <issue_description>### Documentation issue
> 
> <!-- (Update "[ ]" to "[x]" to check a box) -->
> 
> - [ ] Reporting a typo
> - [ ] Reporting a documentation bug
> - [ ] Documentation improvement
> - [x] Documentation feedback
> 
> <!--
> If your issue is not regarding the documentation, please choose an
issue type:
>   https://github.com/nrwl/nx/issues/new/choose
> -->
> 
> ### Is there a specific documentation page you are reporting?
> 
>
https://nx.dev/docs/technologies/dotnet/guides/incremental-builds#target-dependencies
> 
> ### Additional context or description
> 
> The code sample provides in this doc includes `"dependsOn":
["restore", "^build"]`, but the automatically inferred `build` target
from this plugin does not actually include the `restore` target in the
dependsOn array. I assume this is by design? The docs seem to confuse it
a bit.
> </issue_description>
> 
> <agent_instructions>Remove the `restore` target from the dependsOn
block. Add a small explanation that we can't run restore through Nx
because Nx requires restore to have been completed prior to running
tasks if the solution uses a custom framework</agent_instructions>
> 
> ## Comments on the Issue (you are @copilot in this section)
> 
> <comments>
> </comments>
> 


</details>



<!-- START COPILOT CODING AGENT SUFFIX -->

- Fixes nrwl/nx#34150

<!-- START COPILOT CODING AGENT TIPS -->
---

💬 We'd love your input! Share your thoughts on Copilot coding agent in
our [2 minute survey](https://gh.io/copilot-coding-agent-survey).

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: AgentEnder <6933928+AgentEnder@users.noreply.github.com>
2026-02-02 08:55:21 -06:00
iceThief (민찬기) e35dcd2050 fix(core): handle multibyte UTF-8 characters in socket message consumption (#34151)
## Current Behavior

When socket data chunks split a multibyte UTF-8 character (e.g., CJK
characters like Korean, Chinese, Japanese) at an arbitrary byte
boundary, `Buffer.toString()` decodes incomplete byte sequences as
replacement characters (�), causing message corruption.

This can occur when:
- File paths contain non-ASCII characters
- Project names include multibyte characters
- Any JSON message contains international text

## Expected Behavior

Multibyte UTF-8 characters should be properly decoded even when split
across multiple socket data chunks. The fix uses Node.js `StringDecoder`
which buffers incomplete multibyte sequences until the remaining bytes
arrive.

## Related Issue(s)

Fixes socket message corruption for paths/names containing multibyte
characters.
2026-02-01 22:09:53 -05:00
Caleb Ukle 251121530d fix(nx-dev): make headers and table options linkable (#34267)
- fix(nx-dev): always link headers regardless of mdoc or markdown
content source (generated vs static file)
- fix(nx-dev): make option/property columns in table linkable
- the table column header is matched on `options`, `option`,
`properties`, and property` (case insensitive)



https://github.com/user-attachments/assets/7250b9d5-1030-4ebc-9e21-0a05f295bbf5


Note bc mdoc and `renderMarkdown` go through 2 different rendering
pipelines, this logic must bc within the markdoc config and rehype
(markdown) processing logic. tried to shared logic where I could

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-30 18:15:46 -05:00
Jack Hsu ec110b7bff feat(core): add decorative banners for Nx Cloud CNW completion message (#34270)
## Current Behavior

After completing the CNW (Create Nx Workspace) flow with Nx Cloud, users
see a plain text completion message with a link to finish setup.

## Expected Behavior

Users now see one of four completion message variants controlled by
`NX_CNW_FLOW_VARIANT`:
- **Variant 0**: Plain link (control) - always used for enterprise URLs
- **Variant 1**: "Try the full Nx platform" decorative ASCII banner
- **Variant 2**: "Unlock 70% faster CI" decorative ASCII banner
- **Variant 3**: "Reclaim your team's focus" decorative ASCII banner

Key changes:
- Added enterprise URL detection (non-standard Nx Cloud URLs always get
variant 0)
- Locked the cloud prompt to always show "Try the full Nx platform?" (no
longer varies by flow variant)
- Flow variant now only affects the completion banner, not the prompt
- Added `snapshot.nx.app` to standard Nx Cloud hosts
- Removed variant 2 auto-connect behavior (all variants now prompt)

## Screenshots
Variant 0:
<img width="1392" height="1065" alt="variant0"
src="https://github.com/user-attachments/assets/0b18686e-1481-4fc0-995e-1577052887ff"
/>

Variant 1:
<img width="1392" height="1065" alt="variant1"
src="https://github.com/user-attachments/assets/e5909e2e-e1d8-4d04-9721-ba6186d06891"
/>

Variant 2:
<img width="1392" height="1065" alt="variant2"
src="https://github.com/user-attachments/assets/7e9f819f-e3c0-44d7-9760-0cab1d5dd9ac"
/>

Variant 3:
<img width="1392" height="1065" alt="variant3"
src="https://github.com/user-attachments/assets/83f0499f-d807-4dc8-9390-c7eec93590a9"
/>


## Related Issue(s)

Closes CLOUD-4147

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-30 22:24:18 +00:00
Louie Weng bd627f1096 chore(repo): enable batch mode (#34245)
<!-- Please make sure you have read the submission guidelines before
posting an PR -->
<!--
https://github.com/nrwl/nx/blob/master/CONTRIBUTING.md#-submitting-a-pr
-->

<!-- Please make sure that your commit message follows our format -->
<!-- Example: `fix(nx): must begin with lowercase` -->

<!-- If this is a particularly complex change or feature addition, you
can request a dedicated Nx release for this pull request branch. Mention
someone from the Nx team or the `@nrwl/nx-pipelines-reviewers` and they
will confirm if the PR warrants its own release for testing purposes,
and generate it for you if appropriate. -->

## Current Behavior
<!-- This is the behavior we have today -->

## Expected Behavior
<!-- This is the behavior we should expect with the changes in this PR
-->

Enable Gradle executor to run tasks in batch mode in CI.

## Related Issue(s)
<!-- Please link the issue being fixed so it gets closed when this is
merged. -->

Fixes #
2026-01-30 21:25:51 +00:00
Victor Savkin e3eedf9e94 docs(misc): update the docs to use more direct language (#34264)
<!-- Please make sure you have read the submission guidelines before
posting an PR -->
<!--
https://github.com/nrwl/nx/blob/master/CONTRIBUTING.md#-submitting-a-pr
-->

<!-- Please make sure that your commit message follows our format -->
<!-- Example: `fix(nx): must begin with lowercase` -->

<!-- If this is a particularly complex change or feature addition, you
can request a dedicated Nx release for this pull request branch. Mention
someone from the Nx team or the `@nrwl/nx-pipelines-reviewers` and they
will confirm if the PR warrants its own release for testing purposes,
and generate it for you if appropriate. -->

## Current Behavior
<!-- This is the behavior we have today -->

## Expected Behavior
<!-- This is the behavior we should expect with the changes in this PR
-->

## Related Issue(s)
<!-- Please link the issue being fixed so it gets closed when this is
merged. -->

Fixes #
2026-01-30 14:43:59 -05:00
Jack Hsu eb678bfa59 docs(misc): push toc to the right side to match the alignment of the left sidebar (#34266)
This PR aligns the TOC to the right side of the page so the spacing is
more balanced.

<img width="2672" height="1527" alt="image"
src="https://github.com/user-attachments/assets/ef00906a-3788-47fb-a71d-230b3d69c201"
/>

Similar to other docs like React:

<img width="2672" height="1527" alt="image"
src="https://github.com/user-attachments/assets/e9a1cd81-f716-4e09-b148-be80a93b7aee"
/>


---

## Other screen widths

1400px:

<img width="1424" height="1025" alt="Screenshot 2026-01-30 at 12 11
57 PM"
src="https://github.com/user-attachments/assets/d9fc5029-4942-4e32-b2f2-4c66ebcb21df"
/>

1000px (TOC hidden):


<img width="1145" height="1019" alt="Screenshot 2026-01-30 at 12 12
10 PM"
src="https://github.com/user-attachments/assets/ce1cd890-193a-42d7-bb4f-3259146845a8"
/>
2026-01-30 12:42:13 -05:00
Jack Hsu dc8839365f docs(misc): reduce memory footprint of nx-dev build (#34258)
We're showing over 8 GB of memory usage on Netlify, and 11+ GB on
Agents. Let's test out a few ways to reduce the memory footprint.

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-30 10:53:21 -05:00
Richard Roncancio 3f05214013 chore(release): Improve release performance (#33866) 2026-01-30 18:15:49 +04:00
Colum Ferry 86de174d86 fix(testing): preload vitest/node to prevent race condition on Node 24 (#34261)
Preload vitest/node ESM module early in
buildViteTargets/buildVitestTargets
functions before parallel processing occurs. This prevents the
ERR_INTERNAL_ASSERTION error that occurs when multiple vitest.config
files
are processed in parallel on Node 24+.

Fixes #34028
Fixes #33091
2026-01-30 13:33:11 +00:00
Jason Jean d9f2ed0d44 fix(testing): add timeout to runCommandUntil to prevent hanging tests (#34148)
## Current Behavior

The `runCommandUntil` e2e utility function waits indefinitely for the
expected output to appear. If the output never appears (e.g., server
fails to start, different output format, port conflict), the test hangs
forever, causing CI jobs to run for hours before being killed.

## Expected Behavior

The function should timeout after a configurable duration and fail with
a clear error message showing what output was received.

## Related Issue(s)

Fixes hanging e2e tests observed in CI (e.g.,
`e2e-node:e2e-ci--src/node-server.test.ts` hung for 1h 21m).

## Changes

- Added optional `timeout` parameter to `runCommandUntil` opts (default:
5 seconds)
- On timeout: kills the process, logs the collected output, and rejects
with a clear error
- Existing call sites work unchanged; tests needing more startup time
can pass `{ timeout: 30000 }`

---------

Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>
Co-authored-by: FrozenPandaz <8104246+FrozenPandaz@users.noreply.github.com>
Co-authored-by: nx-cloud[bot] <71083854+nx-cloud[bot]@users.noreply.github.com>
Co-authored-by: FrozenPandaz <FrozenPandaz@users.noreply.github.com>
2026-01-30 00:18:26 -05:00
Jack Hsu c331bf949c fix(nx-dev): fix internal link check caching and remaining /launch-nx link (#34255)
## Current Behavior

1. The `check-links` task cache inputs only included `sitemap.xml` (the
index
file) and `sitemap-index.xml`, but not the actual `sitemap-0.xml` files
that
contain the URL data. This meant that when pages were added or removed,
the
cache wasn't properly invalidated - the check-links task would return a
   cached "passing" result even when broken links existed.

2. The `/launch-nx` page was removed in #34183 but one link in
`astro-docs/src/content/docs/reference/Nx Cloud/release-notes.mdoc`
still
pointed to it. This link was masked by being in the `validate-links.ts`
   ignore list.

## Expected Behavior

1. The `check-links` task cache is invalidated when sitemap URLs change
by
   using glob patterns (`sitemap*.xml`) to include all sitemap files.

2. All links point to valid pages. The `/launch-nx` link now redirects
to
   `/blog/launch-nx-week-recap`.

## Changes

- **astro-docs/release-notes.mdoc**: Updated `/launch-nx` link to
`/blog/launch-nx-week-recap`
- **astro-docs/validate-links.ts**: Removed `/launch-nx` from ignore
list (no longer needed)
- **nx-dev/project.json**: Fixed cache inputs to use `sitemap*.xml` glob
patterns

## Related Issue(s)

Fixes DOC-385

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 17:10:47 -05:00
Colum Ferry 3d62c8b5c1 fix(vite): handle sophisticated vite plugins (#34242)
## Current Behavior
With Vite now providing additional options (environments etc) for
framework authors, vite.config files can be much more simple for the
user.
However, this often assumes that the `root` property will be set and
provided during `Vite CLI` invocation.

When we run `resolveConfig` to determine inputs and outputs, we do not
set this `root` and expect the user to have it in their vite config
file.
For some plugins/frameworks such as Tanstack Start - this causes the
plugin to error.

The `isBuildable` conditions is also not inclusive enough and can skip
projects that should be marked as buildable.

## Expected Behavior
Ensure that sophisticated vite plugins are supported with Nx

## Related Issue(s)

CLOSES NXC-3637
2026-01-29 14:28:31 +00:00
Jack Hsu 9c3a9d7e13 feat(core): add Nx Cloud connect URL to template README (#34249)
## Current Behavior
Template-generated workspaces use a generic link in the README instead
of a per-workspace short link for Nx Cloud setup.

## Expected Behavior
When users opt into Nx Cloud (or are auto-connected via variant 2), the
template README is updated with a personalized connect URL section that
helps them finish setting up their workspace.

---
BEFORE: 

<img width="762" height="460" alt="image"
src="https://github.com/user-attachments/assets/58900071-1727-49d1-aa19-279c488b5037"
/>


AFTER: 

<img width="1032" height="599" alt="image"
src="https://github.com/user-attachments/assets/a6e3a122-5807-4ba2-90dd-441e41a3280e"
/>

---

## Related Issue(s)
Closes NXC-3783

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 09:21:20 -05:00
Drew Teachout e189dcc101 fix(core): do not throw error if worker.stdout is not instanceof socket (#34224)
deno worker.stdout is a Readable/Writeable. To provide better deno
support an error should not be thrown

<!-- Please make sure you have read the submission guidelines before
posting an PR -->
<!--
https://github.com/nrwl/nx/blob/master/CONTRIBUTING.md#-submitting-a-pr
-->

<!-- Please make sure that your commit message follows our format -->
<!-- Example: `fix(nx): must begin with lowercase` -->

<!-- If this is a particularly complex change or feature addition, you
can request a dedicated Nx release for this pull request branch. Mention
someone from the Nx team or the `@nrwl/nx-pipelines-reviewers` and they
will confirm if the PR warrants its own release for testing purposes,
and generate it for you if appropriate. -->

## Current Behavior
<!-- This is the behavior we have today -->
If `worker.stdout` or `worker.stderr` are not instanceof Socket then nx
throws an error. This is problematic in Deno where `stdout` and `stderr`
are Readable/Writable and not Socket.

## Expected Behavior
<!-- This is the behavior we should expect with the changes in this PR
-->
`startupPluginWorker` function should work in Deno runtime

## Related Issue(s)
<!-- Please link the issue being fixed so it gets closed when this is
merged. -->

- https://github.com/denoland/deno/issues/31961
- https://github.com/oven-sh/bun/issues/26505

---------

Co-authored-by: Craigory Coppola <craigorycoppola@gmail.com>
2026-01-29 07:21:09 -05:00
Craigory Coppola 4f4b9dc048 fix(core): improve plugin worker error messages and lifecycle timeouts (#34251)
## Current Behavior
Plugin workers occasionally fall over during the start up steps. 

## Expected Behavior
Improves some issues with the error handling when loading plugin workers
and adds some more logs to help understand what's went wrong here.

## Related Issue(s)
<!-- Please link the issue being fixed so it gets closed when this is
merged. -->

Fixes #

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: nx-cloud[bot] <71083854+nx-cloud[bot]@users.noreply.github.com>
Co-authored-by: AgentEnder <AgentEnder@users.noreply.github.com>
2026-01-29 01:57:54 -05:00
Jason Jean fdabc14892 chore(repo): update nx to 22.5.0-beta.2 (#34252)
Updating Nx from 22.5.0-beta.1 to 22.5.0-beta.2

---------

Co-authored-by: nx-cloud[bot] <71083854+nx-cloud[bot]@users.noreply.github.com>
2026-01-29 00:57:04 -05:00
Louie Weng da3b00f961 chore(core): edit project graph aggregate error message (#34248)
<!-- Please make sure you have read the submission guidelines before
posting an PR -->
<!--
https://github.com/nrwl/nx/blob/master/CONTRIBUTING.md#-submitting-a-pr
-->

<!-- Please make sure that your commit message follows our format -->
<!-- Example: `fix(nx): must begin with lowercase` -->

<!-- If this is a particularly complex change or feature addition, you
can request a dedicated Nx release for this pull request branch. Mention
someone from the Nx team or the `@nrwl/nx-pipelines-reviewers` and they
will confirm if the PR warrants its own release for testing purposes,
and generate it for you if appropriate. -->

## Current Behavior
<!-- This is the behavior we have today -->

## Expected Behavior
<!-- This is the behavior we should expect with the changes in this PR
-->

Remove redundantly placed period and make error message construction
more readable when facing AggregateError

## Related Issue(s)
<!-- Please link the issue being fixed so it gets closed when this is
merged. -->

Fixes NXC-3766

---------

Co-authored-by: nx-cloud[bot] <71083854+nx-cloud[bot]@users.noreply.github.com>
2026-01-28 22:19:44 +00:00
Jason Jean 8d5b316fdd chore(repo): update nx to 22.5.0-beta.1 (#34234)
Updating Nx from 22.5.0-beta.0 to 22.5.0-beta.1

---------

Co-authored-by: nx-cloud[bot] <71083854+nx-cloud[bot]@users.noreply.github.com>
2026-01-28 16:39:46 -05:00
Jack Hsu b53fd7c7f5 docs(misc): add 1% scroll depth tracking to docs and non-docs pages (#34246)
This PR adds scroll depth event at the 10% level so we can filter out
users who actually engages with the page versus those who maybe land on
homepage just to get to docs.

<img width="2672" height="1527" alt="Screenshot 2026-01-28 at 4 00
54 PM"
src="https://github.com/user-attachments/assets/ad9be5ae-442c-48eb-9c1e-70f0b872563b"
/>


Also fix the scroll tracker for astro-docs.

<img width="2672" height="1527" alt="Screenshot 2026-01-28 at 4 00
54 PM"
src="https://github.com/user-attachments/assets/e4a24ef2-b6aa-4f5a-b10b-972b73385fee"
/>

<img width="2672" height="1527" alt="Screenshot 2026-01-28 at 2 39
09 PM"
src="https://github.com/user-attachments/assets/833c71f1-7a96-4637-bb6e-3b21227053f0"
/>


## Related Issue(s)
Closes CLOUD-4211

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-28 16:25:12 -05:00
Louie Weng 5a424fa8df fix(gradle): use tooling api compatible flags (#34247)
<!-- Please make sure you have read the submission guidelines before
posting an PR -->
<!--
https://github.com/nrwl/nx/blob/master/CONTRIBUTING.md#-submitting-a-pr
-->

<!-- Please make sure that your commit message follows our format -->
<!-- Example: `fix(nx): must begin with lowercase` -->

<!-- If this is a particularly complex change or feature addition, you
can request a dedicated Nx release for this pull request branch. Mention
someone from the Nx team or the `@nrwl/nx-pipelines-reviewers` and they
will confirm if the PR warrants its own release for testing purposes,
and generate it for you if appropriate. -->

## Current Behavior
<!-- This is the behavior we have today -->

## Expected Behavior
<!-- This is the behavior we should expect with the changes in this PR
-->

--rerun is not tooling api compatible and therefore will break usage of
the batch executor. Replaced the flag with --rerun-tasks.

## Related Issue(s)
<!-- Please link the issue being fixed so it gets closed when this is
merged. -->

Fixes #
2026-01-28 21:23:21 +00:00
Jason Jean cf043b7e20 feat(maven): load Maven classes at runtime for version-agnostic batch execution (#34180)
## Current Behavior

The Maven batch runner bundles Maven 4 classes at compile time, which:
- Creates a large JAR file (~50+ MB)
- Only works with Maven 4
- Has classloader conflicts with Maven's own SLF4J

## Expected Behavior

The batch runner loads Maven classes at runtime from `MAVEN_HOME`,
which:
- Creates a small JAR (~2 MB) with no bundled Maven dependencies
- Works with both Maven 3.x and Maven 4.x
- Avoids classloader conflicts by isolating Maven in its own ClassRealm
- Outputs clean Maven-style logs (`[INFO]`, `[WARNING]`, etc.)

## Implementation

### Architecture

```
batch-runner.jar (NO Maven dependencies)
├── MavenClassRealm         → Loads Maven JARs from MAVEN_HOME at runtime
├── ResidentMavenExecutor   → Maven 4 executor (reflection-based)
├── CachingMaven3Invoker    → Maven 3 executor (reflection-based)
└── nx-maven-adapters/      → Pre-compiled adapter JARs (embedded as resources)
    ├── batch-runner-adapters-maven3.jar
    └── batch-runner-adapter-maven4.jar
```

### Key Changes

1. **Removed compile-time Maven dependencies** from batch-runner module
2. **Created batch-runner-adapters** modules for Maven 3 and Maven 4
specific code
3. **Implemented MavenClassRealm** to load Maven JARs from MAVEN_HOME at
runtime
4. **Implemented reflection-based executors** that load adapter JARs
into ClassRealm
5. **Fixed SLF4J logging** with System.out redirection for clean
Maven-style output
6. **Added shared module** for BuildStateManager, BuildStateApplier, and
BuildStateRecorder

### Benefits

- **Version agnostic**: Same JAR works with Maven 3.x and 4.x
- **Graph caching**: Project dependency graph built once, reused across
tasks
- **Build state persistence**: compile → package → install works
correctly
- **No classloader conflicts**: Maven's classes isolated in their own
ClassRealm
- **Clean output**: Standard Maven log format without SLF4J noise

## Related Issue(s)

N/A - Internal refactoring for better Maven version support

---------

Co-authored-by: nx-cloud[bot] <71083854+nx-cloud[bot]@users.noreply.github.com>
Co-authored-by: FrozenPandaz <FrozenPandaz@users.noreply.github.com>
2026-01-28 14:13:31 -05:00
Jack Hsu 7276e4cff7 docs(misc): content negotiation for LLM-friendly docs access (#34239)
## Current Behavior
LLMs and CLI tools must explicitly request the `.md` URL suffix to get
raw markdown content from documentation pages.

## Expected Behavior
When a client requests a docs page with `Accept: text/markdown` header,
the edge function rewrites to serve the `.md` version directly (no
redirect). This enables LLM tools to get markdown content by requesting
the standard URL.

Behavior:
- `Accept: text/markdown` → serves .md content (via rewrite, no
redirect)
- Default (browsers) → serves HTML with Link headers (unchanged)

Examples:
```
curl -H 'Accept: text/markdown' https://deploy-preview-34239--nx-docs.netlify.app/docs/getting-started/intro
curl -H 'Accept: text/markdown' https://deploy-preview-34239--nx-docs.netlify.app/docs/getting-started/tutorials/angular-monorepo-tutorial
```

Uses Netlify Edge Function rewrite (returns URL object) instead of
redirect for single-request response that works with all HTTP clients.

## Related Issue(s)
Closes DOC-389

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-28 09:06:10 -05:00
Caleb Ukle f9258c9d82 fix(nx-dev): update dead links across nx-dev UI libraries (#34238)
## Current Behavior

broken links

## Expected Behavior

links aren't broken any more and are updated to expected pages from docs
page.

## Related Issue(s)

Fixes DOC-391
2026-01-27 15:51:55 -06:00
Jack Hsu 8b245f1b0a feat(nx-dev): add llms-full.txt and HTTP Link headers for LLM discovery (#34232)
This PR adds:
1. `llms-full.txt` that is a full copy of our docs in markdown.
2. HTTP `Link` headers to our docs HTML pages so that they point to the
`.md` (markdown) version, and also to `llms.txt` and `llms-full.txt`.

The `llms-full.txt` is currently at 2.7 MB, which is much less than
other sites that are up to 5MB or more.

<img width="569" height="35" alt="Screenshot 2026-01-27 at 12 11 10 PM"
src="https://github.com/user-attachments/assets/61be02b4-2813-4c39-951c-d831af83e823"
/>

First 100 lines of `llms-full.txt`:

````
# Nx Documentation

> Complete Nx documentation compiled into a single file for LLM consumption.

Nx is a powerful, open source, technology-agnostic build platform designed to efficiently manage codebases of any scale. From small single projects to large enterprise monorepos, Nx provides intelligent task execution, caching, and CI optimization.

This file was generated from 503 documentation pages.
Individual pages are available at: https://nx.dev/docs/{slug}.md


# Quickstart

---
<!-- source: https://nx.dev/docs/quickstart.md -->
## Quickstart with Nx

Get up and running with Nx in just a few minutes by following these simple steps.

{% steps %}

1. Install the Nx CLI

   Installing Nx globally is **optional** - you can use `npx` to run Nx commands without installing it globally, especially if you're working with Node.js projects.

   {% tabs syncKey="install-method" %}
   {% tabitem label="npm" %}

   ```shell
   npm add --global nx
   ```

   **Note:** You can also use Yarn, pnpm, or Bun

   {% /tabitem %}
   {% tabitem label="Homebrew (macOS, Linux)" %}

   ```shell
   brew install nx
   ```

   {% /tabitem %}
   {% tabitem label="Chocolatey (Windows)" %}

   ```shell
   choco install nx
   ```

   {% /tabitem %}
   {% tabitem label="apt (Ubuntu)" %}

   ```shell
   sudo add-apt-repository ppa:nrwl/nx
   sudo apt update
   sudo apt install nx
   ```

   {% /tabitem %}
   {% /tabs %}

2. Start fresh or add to existing project

   For JavaScript-based projects you can **start with a new workspace** using the following command:

   ```shell
   npx create-nx-workspace@latest
   ```

   **Add to an existing project: (recommended also for non-JS projects)**

   ```shell
   npx nx@latest init
   ```

   **Get the complete experience:**
   For a fully integrated development workflow with AI-powered CI features, [start directly from Nx Cloud](https://cloud.nx.app/get-started).

   Learn more: [Start New Project](/docs/getting-started/start-new-project) • [Add to Existing](/docs/getting-started/start-with-existing-project) • [Complete Nx Experience](https://cloud.nx.app/get-started)

3. Run Your First Commands

   Nx provides powerful task execution with built-in caching. Here are some essential commands:

   **Run a task for a single project:**

   ```shell
   nx build my-app
   nx test my-lib
   ```

   **Run tasks for multiple projects:**

   ```shell
   nx run-many -t build test lint
   ```

   Learn more: [Run Tasks](/docs/features/run-tasks) • [Cache Task Results](/docs/features/cache-task-results)

4. What's next?

   Now that you've experienced the Nx basics, choose how you want to continue:

````


## Related Issue(s)
Closes DOC-236

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: nx-cloud[bot] <71083854+nx-cloud[bot]@users.noreply.github.com>
2026-01-27 16:01:48 -05:00
Benjamin Cabanes a092ed06c4 docs(nx-dev): migrate color variants to contrast design (#34077)
This design refresh emphasizes the contrast variant aesthetic across all
hero sections, pricing cards, and primary call-to-actions.

- Change 47 instances of variant="primary" to variant="contrast"
- Update ui-courses to use variant="secondary" for GitHub link
- Prefer high-contrast inverted style for primary CTAs
- Maintain proper visual hierarchy with secondary actions
- Replace all slate-* classes with zinc-* equivalents (1,158 instances)
- Replace all sky-* classes with blue-* equivalents (210 instances)
- Update opacity variants, gradients, rings, and borders
- Maintain full dark mode compatibility
2026-01-27 20:54:33 +00:00
MaxKless de67da4bc6 chore(repo): add update ai agents configuration for a bunch of ai agents (#34231)
this sets the nx repo up with the latest and greatest
2026-01-27 13:50:42 -05:00
MaxKless f89ccb091f fix(core): hide already-installed nx packages from suggestion list during nx import (#34227)
## Current Behavior
If something is in `package.json#dependencies`, we still suggest it to
be `nx add`-ed during `nx import`

## Expected Behavior
If a plugin is already installed, we don't suggest it anymore
2026-01-27 13:23:42 -05:00
Colum Ferry 2bd8ef3f72 feat(js): bump swc to latest versions (#34215)
## Current Behavior
SWC versions are a few minors behind.

## Expected Behavior
SWC versions are up to date and are being managed via PNPM Catalogs
2026-01-27 17:14:55 +00:00
MaxKless 0e8893faea feat(core): improve configure-ai-agents to copy nx skills/subagents/plugins (#34176)
## Current Behavior
The `configure-ai-agents` command sets up rules files (CLAUDE.md,
AGENTS.md, GEMINI.md) and MCP configurations for AI coding agents, but
doesn't provide extensibility artifacts like commands, skills, or
subagents.
## Expected Behavior
The command now:
- **Adds OpenCode** as a new supported agent with project-level MCP
config
- **Configures Claude plugin** via marketplace settings
(`.claude/settings.json` with `extraKnownMarketplaces`)
- **Copies extensibility artifacts** (commands, skills, subagents) from
`nrwl/nx-ai-agents-config` repo for non-Claude agents
- **Caches the config repo** in
`/tmp/nx-ai-agents-config/<commit-hash>/` with automatic cleanup of old
versions
### Agent Distribution Matrix
| Agent | Rules | MCP Config | Commands | Skills | Subagents | Plugin |
|-------|-------|------------|----------|--------|-----------|--------|
| Claude | CLAUDE.md | .mcp.json | - | - | - | ✓ (marketplace) |
| OpenCode | AGENTS.md | opencode.json | ✓ | ✓ | ✓ | - |
| Copilot | AGENTS.md | Nx Console | ✓ | ✓ | ✓ | - |
| Cursor | AGENTS.md | Nx Console | ✓ | ✓ | - | - |
| Gemini | GEMINI.md | .gemini/settings.json | ✓ | ✓ | - | - |

---------

Co-authored-by: nx-cloud[bot] <71083854+nx-cloud[bot]@users.noreply.github.com>
Co-authored-by: MaxKless <MaxKless@users.noreply.github.com>
Co-authored-by: FrozenPandaz <jasonjean1993@gmail.com>
2026-01-27 16:10:28 +00:00
Jason Jean f9ab939c74 chore(repo): update nx to 22.5.0-beta.0 (#34209)
Updating Nx from 22.4.0-beta.5 to 22.5.0-beta.0
2026-01-27 09:52:22 -05:00
Colum Ferry 7a446e4979 fix(web): ensure vitest config file is created (#34216)
`@nx/web:app` generator is incorrectly calling `createOrEditViteConfig`
when bundler != vite and unitTestRunner = vitest.

Ensure it is using the correct file
2026-01-27 09:41:21 +00:00
Jack Hsu 0f33fa6c40 feat(core): add variant 2 to CNW cloud prompts with promo message (#34223)
This PR uses three variants for CNW for prompting for Cloud/platform
connection.

- Variant 0: Shows "Try the full Nx platform?" prompt → platform-setup
completion
- Variant 1: Shows "Would you like remote caching..." prompt →
cache-setup completion
- Variant 2: No prompt → platform-promo completion with "Want faster
builds?"

Both template and custom flows use the same messages and prompts.


### Skip (all) -- No changes


<img width="1392" height="994" alt="cnw_all_skip_completion"
src="https://github.com/user-attachments/assets/1579df01-fc36-4324-bc74-19efc18f78af"
/>

### Variant 0 (full platform)

Template prompt:

<img width="1392" height="994" alt="cnw_template_variant_0_prompt"
src="https://github.com/user-attachments/assets/d02e9ddc-fd25-4f6f-ac61-6f6d518fe338"
/>

Template completion:

<img width="1392" height="994" alt="cnw_template_variant_0_completion"
src="https://github.com/user-attachments/assets/12e459a0-962c-4724-82fa-a4f5a3b6fbd8"
/>

Custom prompt:

<img width="1392" height="994" alt="cnw_custom_variant_0_prompt"
src="https://github.com/user-attachments/assets/8143832a-a85e-43b4-81eb-15074c223476"
/>

Custom completion:

<img width="1392" height="994" alt="cnw_custom_variant_0_completion"
src="https://github.com/user-attachments/assets/036ef6f9-4977-459a-bbd2-502670a12d01"
/>

### Variant 1 (remote cache)

Template prompt:

<img width="1392" height="994" alt="cnw_template_variant_1_prompt"
src="https://github.com/user-attachments/assets/f69b540f-7e48-46ae-99e5-f53657176424"
/>

Template completion:

<img width="1392" height="994" alt="cnw_template_variant_1_completion"
src="https://github.com/user-attachments/assets/04c17847-b0f2-4e37-b7d2-5556aa14f510"
/>

Custom prompt:

<img width="1392" height="994" alt="cnw_custom_variant_1_prompt"
src="https://github.com/user-attachments/assets/abdb38b2-611e-458b-85c0-7b89cd1b827e"
/>


Custom completion:

<img width="1348" height="950" alt="cnw_custom_variant_1_completion"
src="https://github.com/user-attachments/assets/e7d35ddb-e8ac-4ad1-889e-9fff89538900"
/>

## Variant 2 (no prompt)

Template completion:

<img width="1392" height="994" alt="cnw_template_variant_2_completion"
src="https://github.com/user-attachments/assets/07050989-a56c-46eb-a0c3-8730916856ff"
/>

Custom completion:

<img width="1392" height="994" alt="cnw_custom_variant_2_completion"
src="https://github.com/user-attachments/assets/94000828-37b2-4059-a13b-0d36972cfd3e"
/>

## Related Issue(s)

Closes CLOUD-4189

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-26 16:30:05 -05:00
Jack Hsu e108dac1bb Revert "Revert "feat(core): add A/B testing variant 1 to skip cloud p…rompt in CNW (#34106)" (#34191) (#34204)
This reverts commit f016664557.
2026-01-26 14:58:26 -05:00
Colum Ferry 3fcd2008ef fix(react): remove file-loader dependency and update svgr migration (#34218)
## Current Behavior
The migration for svgr requires using file-loader which is unmaintained.

## Expected Behavior
Use asset/resource instead of file-loader

## Related Issue(s)

CLOSES NXC-3667
2026-01-26 16:56:18 +00:00
Jason Jean 75f36edb8f fix(core): fall back to node_modules when tmp has noexec (#34207)
## Summary

- When `/tmp` is mounted with `noexec`, loading native modules from the
cache fails silently and causes Nx to hang indefinitely
- This adds a fallback to load from `node_modules` when permission
errors occur

## Problem

Users with `/tmp` mounted with `noexec` (a common security hardening
practice) experience Nx hanging forever, even for simple commands like
`nx --version`.

The root cause:
1. Nx copies native `.node` files to `/tmp` to avoid Windows file
locking issues
2. On `noexec` mounts, execution fails with `EACCES`/`EPERM`
3. The error wasn't caught, leading to broken native bindings and
infinite loops

## Solution

Catch permission errors when loading from the cache and fall back to the
original `node_modules` location. This:
- Works automatically without user config
- Preserves Windows file locking fix (only falls back when needed)
- No error messages for users

Closes #33991
2026-01-23 17:32:15 -05:00
Miguel 3672e1a3ea fix(devkit): allow null values in JSON schema validation (#34167)
<!-- Please make sure you have read the submission guidelines before
posting an PR -->
<!--
https://github.com/nrwl/nx/blob/master/CONTRIBUTING.md#-submitting-a-pr
-->

<!-- Please make sure that your commit message follows our format -->
<!-- Example: `fix(nx): must begin with lowercase` -->

<!-- If this is a particularly complex change or feature addition, you
can request a dedicated Nx release for this pull request branch. Mention
someone from the Nx team or the `@nrwl/nx-pipelines-reviewers` and they
will confirm if the PR warrants its own release for testing purposes,
and generate it for you if appropriate. -->

## Current Behavior
<!-- This is the behavior we have today -->

Schema validation (done, for instance, when calling an executor) fails
when an option has value "null" and schema accepts null values. I had it
in a custom executor for `nx-release-publish`, that understands that
`nxReleaseVersionData` is implicitly passed, so I define its schema:
```json
"newVersion": {
  "type": ["string", "null"],
  "description": "The new version of the project, null if no changes detected"
}
```

My code calls `getReleaseClient().releaseVersion(options)`, which gets
me a `projectsVersionData` object with version info. It contains `null`
values (allowed). I then pass it, and ends up in:
```typescript
 // nx/src/tasks-runner/task-orchestrator.ts:531-539                                                                                                                                                                      
  const combinedOptions = combineOptionsForExecutor(                                                                                                                                                                       
      task.overrides,  // ← Contains nxReleaseVersionData with null values                                                                                                                                                 
      task.target.configuration,                                                                                                                                                                                           
      targetConfiguration,                                                                                                                                                                                                 
      schema,           // ← Schema from executor                                                                                                                                                                          
      task.target.project,                                                                                                                                                                                                 
      relativeCwd,                                                                                                                                                                                                         
      isVerbose                                                                                                                                                                                                            
  );
```

which fails inside:
```typescript
 // nx/src/utils/params.js:126-201                                                                                                                                                                                        
  function validateObject(opts, schema, definitions) {                                                                                                                                                                     
      // Line 191-200: Iterate through all properties                                                                                                                                                                      
      Object.keys(opts).forEach((p) => {                                                                                                                                                                                   
          validateProperty(                                                                                                                                                                                                
              p,                              // "nxReleaseVersionData"                                                                                                                                                    
              opts[p],                        // { foo: { newVersion: null, ... }}                                                                                                                                         
              (schema.properties ?? {})[p],   // schema for nxReleaseVersionData                                                                                                                                           
              definitions                                                                                                                                                                                                  
          );                                                                                                                                                                                                               
      });                                                                                                                                                                                                                  
  }
  ```

## Expected Behavior
<!-- This is the behavior we should expect with the changes in this PR -->

`null` values should be considered, as they are valid in JSON schemas. It was probably not considered, because we never think that `typeof null === "object"`, but it's unfortunately the case.

## Related Issue(s)
<!-- Please link the issue being fixed so it gets closed when this is merged. -->

I will create one

Fixes https://github.com/nrwl/nx/issues/34169
2026-01-23 15:09:09 -05:00
Jack Hsu bc9b3229c6 feat(js): add NX_PREFER_NODE_STRIP_TYPES to use Node's strip types feature instead of transpilation for TypeScript files (#34202)
Transpiling through SWC or ts-node is slow compared to the native type
stripping that Node.js provides.

This PR adds `NX_PREFER_NODE_STRIP_TYPES` to allow users to use Node.js
built-in TypeScript support. There are some features that need
transpilation that won't work with type stripping:

- Enum declarations
- namespace with runtime code
- legacy module with runtime code
- parameter properties
- path aliases

See: https://nodejs.org/api/typescript.html#full-typescript-support

The speed-up is significant. My test workspace went from 22s to ~2s to
compute from cold cache.

Demo: https://www.loom.com/share/ce1db29e501b46d58109ffeec8a7a649

In the future we should enable this by default, and users have to turn
it off to use SWC/ts-node.

---------

Co-authored-by: nx-cloud[bot] <71083854+nx-cloud[bot]@users.noreply.github.com>
Co-authored-by: jaysoo <jaysoo@users.noreply.github.com>
2026-01-23 14:13:00 -05:00
Leosvel Pérez Espinosa a15881db1a feat(core): display batch tasks in the tui (#33695)
Adds support for Batch tasks and displays them in the TUI.

---------

Co-authored-by: FrozenPandaz <jasonjean1993@gmail.com>
2026-01-23 13:48:04 -05:00
Jack Hsu 1dd4262336 docs(misc): collect usage data on .md and .txt files (#34203)
Right now GA only collects page views. This PR allow us to see which
`.md` files are being used. There are mostly useful for AI agents to
fetch without using too many tokens. We want to track usage so we can
see what techniques to guide agents actually work.

## Related Issue(s)
Closes DOC-386

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-23 12:38:17 -05:00
Jack Hsu 687357c82e fix(core): cloud commands are noop when not connected rather than errors (#34193)
This PR makes it so Cloud commands like `npx nx record` and `npx nx
fix-ci` still work without `nxCloudId`. We'll log a warning so that
`ci.yml` using these commands will still work. The warning let's users
know that these do not work without being connected.

Closes #NXC-3753
2026-01-23 12:29:41 -05:00
Mark Lindsey 3d1e544812 chore(repo): commit lint mention that commits should be lowercase (#34199)
<!-- Please make sure you have read the submission guidelines before
posting an PR -->
<!--
https://github.com/nrwl/nx/blob/master/CONTRIBUTING.md#-submitting-a-pr
-->

<!-- Please make sure that your commit message follows our format -->
<!-- Example: `fix(nx): must begin with lowercase` -->

<!-- If this is a particularly complex change or feature addition, you
can request a dedicated Nx release for this pull request branch. Mention
someone from the Nx team or the `@nrwl/nx-pipelines-reviewers` and they
will confirm if the PR warrants its own release for testing purposes,
and generate it for you if appropriate. -->

## Current Behavior
Commit linting does not mention requirement for commit message to be
lowercase.
<!-- This is the behavior we have today -->

## Expected Behavior
Hook message should instruct user to use all lowercase for commit
message.
<!-- This is the behavior we should expect with the changes in this PR
-->

## Related Issue(s)
<!-- Please link the issue being fixed so it gets closed when this is
merged. -->

Fixes #
2026-01-23 11:19:42 -05:00
Jack Hsu 9bb69383b8 fix(core): consolidate GitHub URL messaging when gh push fails (#34196)
When `gh repo create` fails, users see two redundant messages. This
consolidates them into a single message with the helpful `?name=...`
parameter in the GitHub URL.

BEFORE: (We show `Could not push. Push repo to complete setup.` and then
`Push your repo (https://github.com/new)...` again at the end)

<img width="1209" height="560" alt="image"
src="https://github.com/user-attachments/assets/e22c012c-8e0f-4ff9-a6c2-de8267b69b5d"
/>

AFTER: (Only show `Could not push` as an info log, and then complete
setup is shown only once at the end)

<img width="1250" height="591" alt="image"
src="https://github.com/user-attachments/assets/3dc075c7-4418-4373-a07a-1b95f71b3b87"
/>

Closes NXC-3754

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-23 11:18:23 -05:00
Jonathan Cammisuli 1831ace87e docs(nx-dev): update docs to include SELF_HEALING.md information (#34200) 2026-01-23 15:12:03 +00:00
Mark Lindsey 478afc7046 docs(nx-dev): add bitbucket to self healing docs (#34198)
<!-- Please make sure you have read the submission guidelines before
posting an PR -->
<!--
https://github.com/nrwl/nx/blob/master/CONTRIBUTING.md#-submitting-a-pr
-->

<!-- Please make sure that your commit message follows our format -->
<!-- Example: `fix(nx): must begin with lowercase` -->

<!-- If this is a particularly complex change or feature addition, you
can request a dedicated Nx release for this pull request branch. Mention
someone from the Nx team or the `@nrwl/nx-pipelines-reviewers` and they
will confirm if the PR warrants its own release for testing purposes,
and generate it for you if appropriate. -->

## Current Behavior
Self-healing docs only reference being supported for GitHub, Azure, and
GitLab.
<!-- This is the behavior we have today -->

## Expected Behavior
We should show instructions for all currently supported vcs providers,
including Bitbucket.
<!-- This is the behavior we should expect with the changes in this PR
-->

## Related Issue(s)
<!-- Please link the issue being fixed so it gets closed when this is
merged. -->

Fixes #
2026-01-23 14:48:04 +00:00
JamesHenry 1299d044eb chore(repo): remove --auto-apply-fixes, it is set in Nx Cloud UI 2026-01-23 15:41:24 +04:00
Craigory Coppola 273a474047 fix(core): handle resizing a bit better for inline_tui (#34006)
## Current Behavior
Resizing the TUI while in inline view kinda breaks things. Its
unfortunate, I'm not sure there's a ton to be done, but this PR explores
some solutions

## Expected Behavior
The TUI is less sensitive to resize events with inline mode

## Related Issue(s)
<!-- Please link the issue being fixed so it gets closed when this is
merged. -->

Fixes #
2026-01-22 18:54:56 -05:00
1182 changed files with 60043 additions and 21211 deletions
+3
View File
@@ -1,3 +1,6 @@
[env]
JEMALLOC_SYS_WITH_MALLOC_CONF = "dirty_decay_ms:1000,muzzy_decay_ms:0"
[build]
target-dir = 'dist/target'
+11
View File
@@ -30,5 +30,16 @@
"enableAllProjectMcpServers": true,
"env": {
"BASH_MAX_TIMEOUT_MS": "1800000"
},
"extraKnownMarketplaces": {
"nx-claude-plugins": {
"source": {
"source": "github",
"repo": "nrwl/nx-ai-agents-config"
}
}
},
"enabledPlugins": {
"nx@nx-claude-plugins": true
}
}
+480
View File
@@ -0,0 +1,480 @@
---
name: ci-watcher
description: Polls Nx Cloud CI pipeline and self-healing status. Returns structured state when actionable. Spawned by /nx-cloud-ci-monitor command to monitor CI Attempt status.
model: fast
---
# CI Watcher Subagent
You are a CI monitoring subagent responsible for polling Nx Cloud CI Attempt status and self-healing state. You report status back to the main agent - you do NOT make apply/reject decisions.
## Your Responsibilities
1. Poll CI status using the `ci_information` MCP tool
2. Implement exponential backoff between polls
3. Return structured state when an actionable condition is reached
4. Track iteration count and elapsed time
5. Output status updates based on verbosity level
## Input Parameters (from Main Agent)
The main agent may provide these optional parameters in the prompt:
| Parameter | Description |
| ------------------- | -------------------------------------------------------- |
| `branch` | Branch to monitor (auto-detected if not provided) |
| `expectedCommitSha` | Commit SHA that should trigger a new CI Attempt |
| `previousCipeUrl` | CI Attempt URL before the action (to detect change) |
| `subagentTimeout` | Polling timeout in minutes (default: 60) |
| `verbosity` | Output level: minimal, medium, verbose (default: medium) |
When `expectedCommitSha` or `previousCipeUrl` is provided, you must detect whether a new CI Attempt has spawned.
## MCP Tool Reference
### `ci_information`
**Input:**
```json
{
"branch": "string (optional, defaults to current git branch)",
"select": "string (optional, comma-separated field names)",
"pageToken": "number (optional, 0-based pagination for long strings)"
}
```
**Output:**
```json
{
"cipeStatus": "NOT_STARTED | IN_PROGRESS | SUCCEEDED | FAILED | CANCELED | TIMED_OUT",
"cipeUrl": "string",
"branch": "string",
"commitSha": "string | null",
"failedTaskIds": "string[]",
"verifiedTaskIds": "string[]",
"selfHealingEnabled": "boolean",
"selfHealingStatus": "NOT_STARTED | IN_PROGRESS | COMPLETED | FAILED | NOT_EXECUTABLE | null",
"verificationStatus": "NOT_STARTED | IN_PROGRESS | COMPLETED | FAILED | NOT_EXECUTABLE | null",
"userAction": "NONE | APPLIED | REJECTED | APPLIED_LOCALLY | APPLIED_AUTOMATICALLY | null",
"failureClassification": "string | null",
"taskOutputSummary": "string | null",
"suggestedFixReasoning": "string | null",
"suggestedFixDescription": "string | null",
"suggestedFix": "string | null",
"shortLink": "string | null",
"couldAutoApplyTasks": "boolean | null",
"confidence": "number | null",
"confidenceReasoning": "string | null"
}
```
**Select Parameter:**
| Usage | Returns |
| --------------- | ----------------------------------------------------------- |
| No `select` | Formatted overview (truncated, not recommended for polling) |
| Single field | Raw value with pagination for long strings |
| Multiple fields | Object with requested field values |
**Field Sets for Efficient Polling:**
```yaml
WAIT_FIELDS:
'cipeUrl,commitSha,cipeStatus'
# Minimal fields for detecting new CI Attempt
LIGHT_FIELDS:
'cipeStatus,cipeUrl,branch,commitSha,selfHealingStatus,verificationStatus,userAction,failedTaskIds,verifiedTaskIds,selfHealingEnabled,failureClassification,couldAutoApplyTasks,shortLink,confidence,confidenceReasoning'
# Status fields for determining actionable state
HEAVY_FIELDS:
'taskOutputSummary,suggestedFix,suggestedFixReasoning,suggestedFixDescription'
# Large content fields - fetch only when returning to main agent
```
## Initial Wait
Before first poll, wait based on context:
- **Fresh start (no expected CIPE):** Wait 60 seconds to allow CI to start
- **Expecting new CIPE:** Wait 30 seconds (action already triggered)
**IMPORTANT:** Always run sleep in foreground, NOT as background command.
```bash
sleep 60 # or 30 if expecting new CIPE (FOREGROUND, not background)
```
## Two-Phase Operation
The subagent operates in one of two modes depending on input:
### Mode 1: Fresh Start (no `expectedCommitSha` or `previousCipeUrl`)
Normal polling - process whatever CIPE is returned by `ci_information`.
### Mode 2: Wait-for-New-CIPE (when `expectedCommitSha` or `previousCipeUrl` provided)
**CRITICAL**: When expecting a new CIPE, the subagent must **completely ignore** the old/stale CIPE. Do NOT process its status, do NOT return actionable states based on it.
#### Phase A: Wait Mode
1. Start a **new-CIPE timeout** timer (default: 30 minutes)
2. On each poll of `ci_information`:
- Check if CIPE is NEW:
- `cipeUrl` differs from `previousCipeUrl`**new CIPE detected**
- `commitSha` matches `expectedCommitSha`**correct CIPE detected**
- If still OLD CIPE: **ignore all status fields**, just wait and poll again
- Do NOT return `fix_available`, `ci_success`, etc. based on old CIPE!
3. Output wait status (see below)
4. If timeout (30 min) reached → return `no_new_cipe`
#### Phase B: Normal Polling (after new CIPE detected)
Once new CIPE is detected:
1. Clear the new-CIPE timeout
2. Switch to normal polling mode
3. Process the NEW CIPE's status normally
4. Return when actionable state reached
### Wait Mode Output
While in wait mode, output clearly that you're waiting (not processing):
```
[CI Monitor] ═══════════════════════════════════════════════════════
[CI Monitor] WAIT MODE - Expecting new CI Attempt
[CI Monitor] Expected SHA: <expectedCommitSha>
[CI Monitor] Previous CI Attempt: <previousCipeUrl>
[CI Monitor] ═══════════════════════════════════════════════════════
[CI Monitor] Polling... (elapsed: 0m 30s)
[CI Monitor] Still seeing previous CI Attempt (ignoring): <oldCipeUrl>
[CI Monitor] Polling... (elapsed: 1m 30s)
[CI Monitor] Still seeing previous CI Attempt (ignoring): <oldCipeUrl>
[CI Monitor] Polling... (elapsed: 2m 30s)
[CI Monitor] ✓ New CI Attempt detected! URL: <newCipeUrl>, SHA: <newCommitSha>
[CI Monitor] Switching to normal polling mode...
```
### Why This Matters (Context Preservation)
**The problem**: Stale CIPE data can be very large:
- `taskOutputSummary`: potentially thousands of characters of build/test output
- `suggestedFix`: entire patch files
- `suggestedFixReasoning`: detailed explanation
If subagent returns stale CIPE data to main agent, it **pollutes main agent's context** with useless information (we already processed that CIPE). This wastes valuable context window.
**Without wait mode:**
1. Poll `ci_information` → get old CIPE with huge data
2. Return to main agent with all that stale data
3. Main agent's context gets polluted with useless info
4. Main agent has to process/ignore it anyway
**With wait mode:**
1. Poll `ci_information` → get old CIPE → **ignore it, don't return**
2. Keep waiting internally (stale data stays in subagent)
3. New CIPE appears → switch to normal mode
4. Return to main agent with only the NEW, relevant CIPE data
## Polling Loop
### Subagent State Management
Maintain internal accumulated state across polls:
```
accumulated_state = {}
```
### Call `ci_information` MCP Tool
**Wait Mode (expecting new CI Attempt):**
```
ci_information({
branch: "<branch_name>",
select: "cipeUrl,commitSha,cipeStatus"
})
```
Only fetch minimal fields needed to detect CI Attempt change. Do NOT fetch heavy fields - stale data wastes context.
**Normal Mode (processing CI Attempt):**
```
ci_information({
branch: "<branch_name>",
select: "cipeStatus,cipeUrl,branch,commitSha,selfHealingStatus,verificationStatus,userAction,failedTaskIds,verifiedTaskIds,selfHealingEnabled,failureClassification,couldAutoApplyTasks,shortLink,confidence,confidenceReasoning"
})
```
Merge response into `accumulated_state` after each poll.
### Analyze Response
**If in Wait Mode** (expecting new CIPE):
1. Check if CIPE is new (see Two-Phase Operation above)
2. If old CIPE → **ignore status**, output wait message, poll again
3. If new CIPE → switch to normal mode, continue below
**If in Normal Mode**:
Based on the response, decide whether to **keep polling** or **return to main agent**.
### Keep Polling When
Continue polling (with backoff) if ANY of these conditions are true:
| Condition | Reason |
| --------------------------------------- | ---------------------------------------- |
| `cipeStatus == 'IN_PROGRESS'` | CI still running |
| `cipeStatus == 'NOT_STARTED'` | CI hasn't started yet |
| `selfHealingStatus == 'IN_PROGRESS'` | Self-healing agent working |
| `selfHealingStatus == 'NOT_STARTED'` | Self-healing not started yet |
| `failureClassification == 'FLAKY_TASK'` | Auto-rerun in progress |
| `userAction == 'APPLIED_AUTOMATICALLY'` | New CI Attempt spawning after auto-apply |
When `couldAutoApplyTasks == true`:
- `verificationStatus` = `NOT_STARTED`, `IN_PROGRESS` → keep polling (verification still in progress)
- `verificationStatus` = `COMPLETED` → return `fix_auto_applying` (auto-apply will happen, main agent spawns wait mode subagent)
- `verificationStatus` = `FAILED`, `NOT_EXECUTABLE` → return `fix_available` (auto-apply won't happen, needs manual action)
### Exponential Backoff
Between polls, wait with exponential backoff:
| Poll Attempt | Wait Time |
| ------------ | ----------------- |
| 1st | 60 seconds |
| 2nd | 90 seconds |
| 3rd+ | 120 seconds (cap) |
Reset to 60 seconds when state changes significantly.
**IMPORTANT:** Run sleep in foreground (NOT as background command). Background sleep causes "What should Claude do?" prompts when completed.
```bash
# Example backoff - run in FOREGROUND
sleep 60 # First wait
sleep 90 # Second wait
sleep 120 # Third and subsequent waits (capped)
```
### Fetch Heavy Fields on Actionable State
Before returning to main agent, fetch heavy fields if the status requires them:
| Status | Heavy Fields Needed |
| ------------------- | ------------------------------------------------------------------------------ |
| `ci_success` | None |
| `fix_auto_applying` | None |
| `fix_available` | `taskOutputSummary,suggestedFix,suggestedFixReasoning,suggestedFixDescription` |
| `fix_failed` | `taskOutputSummary` |
| `no_fix` | `taskOutputSummary` |
| `environment_issue` | None |
| `no_new_cipe` | None |
| `polling_timeout` | None |
| `cipe_canceled` | None |
| `cipe_timed_out` | None |
```
# Example: fetching heavy fields for fix_available
ci_information({
branch: "<branch_name>",
select: "taskOutputSummary,suggestedFix,suggestedFixReasoning,suggestedFixDescription"
})
```
Merge response into `accumulated_state`, then return merged state to main agent.
**Pagination:** Heavy string fields return first page only. If `hasMore` indicated, include in return format so main agent knows more content available.
### Return to Main Agent When
Return immediately with structured state if ANY of these conditions are true:
| Status | Condition |
| ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `ci_success` | `cipeStatus == 'SUCCEEDED'` |
| `fix_auto_applying` | `selfHealingStatus == 'COMPLETED'` AND `couldAutoApplyTasks == true` AND `verificationStatus == 'COMPLETED'` |
| `fix_available` | `selfHealingStatus == 'COMPLETED'` AND `suggestedFix != null` AND (`couldAutoApplyTasks != true` OR `verificationStatus` in (`FAILED`, `NOT_EXECUTABLE`)) |
| `fix_failed` | `selfHealingStatus == 'FAILED'` |
| `environment_issue` | `failureClassification == 'ENVIRONMENT_STATE'` |
| `no_fix` | `cipeStatus == 'FAILED'` AND (`selfHealingEnabled == false` OR `selfHealingStatus == 'NOT_EXECUTABLE'`) |
| `no_new_cipe` | `expectedCommitSha` or `previousCipeUrl` provided, but no new CI Attempt detected after 30 min |
| `polling_timeout` | Subagent has been polling for > configured timeout (default 60 min) |
| `cipe_canceled` | `cipeStatus == 'CANCELED'` |
| `cipe_timed_out` | `cipeStatus == 'TIMED_OUT'` |
## Subagent Timeout
Track elapsed time. If you have been polling for more than **60 minutes** (configurable via main agent), return with `status: polling_timeout`.
## Return Format
When returning to the main agent, provide a structured response with accumulated state:
```
## CI Monitor Result
**Status:** <status>
**Iterations:** <count>
**Elapsed:** <minutes>m <seconds>s
### CI Attempt Details
- **Status:** <cipeStatus>
- **URL:** <cipeUrl>
- **Branch:** <branch>
- **Commit:** <commitSha>
- **Failed Tasks:** <failedTaskIds>
- **Verified Tasks:** <verifiedTaskIds>
### Self-Healing Details
- **Enabled:** <selfHealingEnabled>
- **Status:** <selfHealingStatus>
- **Verification:** <verificationStatus>
- **User Action:** <userAction>
- **Classification:** <failureClassification>
- **Confidence:** <confidence>
- **Confidence Reasoning:** <confidenceReasoning>
### Fix Information (if available)
- **Short Link:** <shortLink>
- **Description:** <suggestedFixDescription>
- **Reasoning:** <suggestedFixReasoning>
### Task Output Summary (first page)
<taskOutputSummary>
[MORE_CONTENT_AVAILABLE: taskOutputSummary, pageToken: 1]
### Suggested Fix (first page)
<suggestedFix>
[MORE_CONTENT_AVAILABLE: suggestedFix, pageToken: 1]
```
### Pagination Indicators
When a heavy field has more content available, append indicator:
```
[MORE_CONTENT_AVAILABLE: <fieldName>, pageToken: <nextPage>]
```
Main agent can fetch additional pages if needed using:
```
ci_information({ select: "<fieldName>", pageToken: <nextPage> })
```
Fields that may have pagination:
- `taskOutputSummary` (reverse pagination - page 0 = most recent)
- `suggestedFix` (forward pagination - page 0 = start)
- `suggestedFixReasoning`
### Return Format for `no_new_cipe`
When returning with `status: no_new_cipe`, include additional context:
```
## CI Monitor Result
**Status:** no_new_cipe
**Iterations:** <count>
**Elapsed:** <minutes>m <seconds>s
### Expected CI Attempt Not Found
- **Expected Commit SHA:** <expectedCommitSha>
- **Previous CI Attempt URL:** <previousCipeUrl>
- **Last Seen CI Attempt URL:** <cipeUrl>
- **Last Seen Commit SHA:** <commitSha>
- **New CI Attempt Timeout:** 30 minutes (exceeded)
### Likely Cause
CI workflow failed before Nx tasks could run (e.g., install step, checkout, auth).
Check your CI provider logs for the commit <expectedCommitSha>.
### Last Known CI Attempt State
- **Status:** <cipeStatus>
- **Branch:** <branch>
```
## Status Reporting (Verbosity-Controlled)
Output is controlled by the `verbosity` parameter from the main agent:
| Level | What to Output |
| --------- | ----------------------------------------------------------------- |
| `minimal` | No intermediate output. Only return final result when actionable. |
| `medium` | Output only on significant state changes (not every poll). |
| `verbose` | Output detailed phase information after every poll. |
### Minimal Verbosity
No output during polling. Poll silently and return when done.
### Medium Verbosity (Default)
Output **only when state changes significantly** to save context tokens:
- `cipeStatus` changes (e.g., IN_PROGRESS → FAILED)
- `selfHealingStatus` changes (e.g., IN_PROGRESS → COMPLETED)
- New CI Attempt detected (in wait mode)
Format: single line, no decorators:
```
[CI Monitor] CI: FAILED | Self-Healing: IN_PROGRESS | Elapsed: 4m
```
### Verbose Verbosity
Output detailed phase box after every poll:
```
[CI Monitor] ─────────────────────────────────────────────────────
[CI Monitor] Iteration <N> | Elapsed: <X>m <Y>s
[CI Monitor]
[CI Monitor] CI Status: <cipeStatus>
[CI Monitor] Self-Healing: <selfHealingStatus>
[CI Monitor] Verification: <verificationStatus>
[CI Monitor] Classification: <failureClassification>
[CI Monitor]
[CI Monitor] → <human-readable phase description>
[CI Monitor] ─────────────────────────────────────────────────────
```
### Phase Descriptions (for verbose output)
| Status Combo | Description |
| ----------------------------------------------------------------------------------------- | ------------------------------------------- |
| `cipeStatus: IN_PROGRESS` | "CI running..." |
| `cipeStatus: NOT_STARTED` | "Waiting for CI to start..." |
| `cipeStatus: FAILED` + `selfHealingStatus: NOT_STARTED` | "CI failed. Self-healing starting..." |
| `cipeStatus: FAILED` + `selfHealingStatus: IN_PROGRESS` | "CI failed. Self-healing generating fix..." |
| `cipeStatus: FAILED` + `selfHealingStatus: COMPLETED` + `verificationStatus: IN_PROGRESS` | "Fix generated! Verification running..." |
| `cipeStatus: FAILED` + `selfHealingStatus: COMPLETED` + `verificationStatus: COMPLETED` | "Fix ready! Verified successfully." |
| `cipeStatus: FAILED` + `selfHealingStatus: COMPLETED` + `verificationStatus: FAILED` | "Fix generated but verification failed." |
| `cipeStatus: FAILED` + `selfHealingStatus: FAILED` | "Self-healing could not generate a fix." |
| `cipeStatus: SUCCEEDED` | "CI passed!" |
## Important Notes
- You do NOT make apply/reject decisions - that's the main agent's job
- You do NOT perform git operations
- You only poll and report state
- Respect the `verbosity` parameter for output (default: medium)
- If `ci_information` returns an error, wait and retry (count as failed poll)
- Track consecutive failures - if 5 consecutive failures, return with `status: error`
- When expecting new CI Attempt, track the 30-minute new-CI-Attempt timeout separately from the main polling timeout
+428
View File
@@ -0,0 +1,428 @@
# CI Monitor Command
You are the orchestrator for monitoring Nx Cloud CI pipeline executions and handling self-healing fixes. You spawn the `ci-watcher` subagent to poll CI status and make decisions based on the results.
## Context
- **Current Branch:** !`git branch --show-current`
- **Current Commit:** !`git rev-parse --short HEAD`
- **Remote Status:** !`git status -sb | head -1`
## User Instructions
**Important:** If user provides specific instructions, respect them over default behaviors described below.
## Configuration Defaults
| Setting | Default | Description |
| ------------------------- | ------------- | ------------------------------------------------------------------- |
| `--max-cycles` | 10 | Maximum CIPE cycles before timeout |
| `--timeout` | 120 | Maximum duration in minutes |
| `--verbosity` | medium | Output level: minimal, medium, verbose |
| `--branch` | (auto-detect) | Branch to monitor |
| `--subagent-timeout` | 60 | Subagent polling timeout in minutes |
| `--fresh` | false | Ignore previous context, start fresh |
| `--auto-fix-workflow` | false | Attempt common fixes for pre-CIPE failures (e.g., lockfile updates) |
| `--new-cipe-timeout` | 10 | Minutes to wait for new CIPE after action |
| `--local-verify-attempts` | 3 | Max local verification + enhance cycles before pushing to CI |
## Nx Cloud Connection Check
**CRITICAL**: Before starting the monitoring loop, verify the workspace is connected to Nx Cloud.
### Step 0: Verify Nx Cloud Connection
1. **Check `nx.json`** at workspace root for `nxCloudId` or `nxCloudAccessToken`
2. **If `nx.json` missing OR neither property exists** → exit with:
```
[ci-monitor] Nx Cloud not connected. Unlock 70% faster CI and auto-fix broken PRs with https://nx.dev/nx-cloud
```
3. **If connected** → continue to main loop
## Session Context Behavior
**Important:** Within a Claude Code session, conversation context persists. If you Ctrl+C to interrupt the monitor and re-run `/ci-monitor`, Claude remembers the previous state and may continue from where it left off.
- **To continue monitoring:** Just re-run `/ci-monitor` (context is preserved)
- **To start fresh:** Use `/ci-monitor --fresh` to ignore previous context
- **For a completely clean slate:** Exit Claude Code and restart `claude`
## Default Behaviors by Status
The subagent returns with one of the following statuses. This table defines the **default behavior** for each status. User instructions can override any of these.
| Status | Default Behavior |
| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `ci_success` | Exit with success. Log "CI passed successfully!" |
| `fix_auto_applying` | Fix will be auto-applied by self-healing. Do NOT call MCP. Record `last_cipe_url`, spawn new subagent in wait mode to poll for new CIPE. |
| `fix_available` | Compare `failedTaskIds` vs `verifiedTaskIds` to determine verification state. See **Fix Available Decision Logic** section below. |
| `fix_failed` | Self-healing failed to generate fix. Attempt local fix based on `taskOutputSummary`. If successful → commit, push, loop. If not → exit with failure. |
| `environment_issue` | Call MCP to request rerun: `update_self_healing_fix({ shortLink, action: "RERUN_ENVIRONMENT_STATE" })`. New CIPE spawns automatically. Loop to poll for new CIPE. |
| `no_fix` | CI failed, no fix available (self-healing disabled or not executable). Attempt local fix if possible. Otherwise exit with failure. |
| `no_new_cipe` | Expected CIPE never spawned (CI workflow likely failed before Nx tasks). Report to user, attempt common fixes if configured, or exit with guidance. |
| `polling_timeout` | Subagent polling timeout reached. Exit with timeout. |
| `cipe_canceled` | CIPE was canceled. Exit with canceled status. |
| `cipe_timed_out` | CIPE timed out. Exit with timeout status. |
| `error` | Increment `no_progress_count`. If >= 3 → exit with circuit breaker. Otherwise wait 60s and loop. |
### Fix Available Decision Logic
When subagent returns `fix_available`, main agent compares `failedTaskIds` vs `verifiedTaskIds`:
#### Step 1: Categorize Tasks
1. **Verified tasks** = tasks in both `failedTaskIds` AND `verifiedTaskIds`
2. **Unverified tasks** = tasks in `failedTaskIds` but NOT in `verifiedTaskIds`
3. **E2E tasks** = unverified tasks where target contains "e2e" (task format: `<project>:<target>` or `<project>:<target>:<config>`)
4. **Verifiable tasks** = unverified tasks that are NOT e2e
#### Step 2: Determine Path
| Condition | Path |
| --------------------------------------- | ---------------------------------------- |
| No unverified tasks (all verified) | Apply via MCP |
| Unverified tasks exist, but ALL are e2e | Apply via MCP (treat as verified enough) |
| Verifiable tasks exist | Local verification flow |
#### Step 3a: Apply via MCP (fully/e2e-only verified)
- Call `update_self_healing_fix({ shortLink, action: "APPLY" })`
- Record `last_cipe_url`, spawn subagent in wait mode
#### Step 3b: Local Verification Flow
When verifiable (non-e2e) unverified tasks exist:
1. **Detect package manager:**
- `pnpm-lock.yaml` exists → `pnpm nx`
- `yarn.lock` exists → `yarn nx`
- Otherwise → `npx nx`
2. **Run verifiable tasks in parallel:**
- Spawn `general` subagents to run each task concurrently
- Each subagent runs: `<pm> nx run <taskId>`
- Collect pass/fail results from all subagents
3. **Evaluate results:**
| Result | Action |
| ------------------------- | ---------------------------- |
| ALL verifiable tasks pass | Apply via MCP |
| ANY verifiable task fails | Apply-locally + enhance flow |
4. **Apply-locally + enhance flow:**
- Run `nx apply-locally <shortLink>`
- Enhance the code to fix failing tasks
- Run failing tasks again to verify fix
- If still failing → increment `local_verify_count`, loop back to enhance
- If passing → commit and push, record `expected_commit_sha`, spawn subagent in wait mode
5. **Track attempts** (wraps step 4):
- Increment `local_verify_count` after each enhance cycle
- If `local_verify_count >= local_verify_attempts` (default: 3):
- Get code in commit-able state
- Commit and push with message indicating local verification failed
- Report to user:
```
[ci-monitor] Local verification failed after <N> attempts. Pushed to CI for final validation. Failed: <taskIds>
```
- Record `expected_commit_sha`, spawn subagent in wait mode (let CI be final judge)
#### Commit Message Format
```bash
git commit -m "fix(<projects>): <brief description>
Failed tasks: <taskId1>, <taskId2>
Local verification: passed|enhanced|failed-pushing-to-ci"
```
### Unverified Fix Flow (No Verification Attempted)
When `verificationStatus` is `FAILED`, `NOT_EXECUTABLE`, or fix has `couldAutoApplyTasks != true` with no verification:
- Analyze fix content (`suggestedFix`, `suggestedFixReasoning`, `taskOutputSummary`)
- If fix looks correct → apply via MCP
- If fix needs enhancement → use Apply Locally + Enhance Flow above
- If fix is wrong → reject via MCP, fix from scratch, commit, push
### Auto-Apply Eligibility
The `couldAutoApplyTasks` field indicates whether the fix is eligible for automatic application:
- **`true`**: Fix is eligible for auto-apply. Subagent keeps polling while verification is in progress. Returns `fix_auto_applying` when verified, or `fix_available` if verification fails.
- **`false`** or **`null`**: Fix requires manual action (apply via MCP, apply locally, or reject)
**Key point**: When subagent returns `fix_auto_applying`, do NOT call MCP to apply - self-healing handles it. Just spawn a new subagent in wait mode.
### Apply vs Reject vs Apply Locally
- **Apply via MCP**: Calls `update_self_healing_fix({ shortLink, action: "APPLY" })`. Self-healing agent applies the fix in CI and a new CIPE spawns automatically. No local git operations needed.
- **Apply Locally**: Runs `nx apply-locally <shortLink>`. Applies the patch to your local working directory and sets state to `APPLIED_LOCALLY`. Use this when you want to enhance the fix before pushing.
- **Reject via MCP**: Calls `update_self_healing_fix({ shortLink, action: "REJECT" })`. Marks fix as rejected. Use only when the fix is completely wrong and you'll fix from scratch.
### Apply Locally + Enhance Flow
When the fix needs enhancement (use `nx apply-locally`, NOT reject):
1. Apply the patch locally: `nx apply-locally <shortLink>` (this also updates state to `APPLIED_LOCALLY`)
2. Make additional changes as needed
3. Commit and push:
```bash
git add -A
git commit -m "fix: resolve <failedTaskIds>"
git push origin $(git branch --show-current)
```
4. Loop to poll for new CIPE
### Reject + Fix From Scratch Flow
When the fix is completely wrong:
1. Call MCP to reject: `update_self_healing_fix({ shortLink, action: "REJECT" })`
2. Fix the issue from scratch locally
3. Commit and push:
```bash
git add -A
git commit -m "fix: resolve <failedTaskIds>"
git push origin $(git branch --show-current)
```
4. Loop to poll for new CIPE
### Environment Issue Handling
When `failureClassification == 'ENVIRONMENT_STATE'`:
1. Call MCP to request rerun: `update_self_healing_fix({ shortLink, action: "RERUN_ENVIRONMENT_STATE" })`
2. New CIPE spawns automatically (no local git operations needed)
3. Loop to poll for new CIPE with `previousCipeUrl` set
### No-New-CIPE Handling
When `status == 'no_new_cipe'`:
This means the expected CIPE was never created - CI likely failed before Nx tasks could run.
1. **Report to user:**
```
[ci-monitor] No CI attempt for <sha> after 10 min. Check CI provider for pre-Nx failures (install, checkout, auth). Last CI attempt: <previousCipeUrl>
```
2. **If user configured auto-fix attempts** (e.g., `--auto-fix-workflow`):
- Detect package manager: check for `pnpm-lock.yaml`, `yarn.lock`, `package-lock.json`
- Run install to update lockfile:
```bash
pnpm install # or npm install / yarn install
```
- If lockfile changed:
```bash
git add pnpm-lock.yaml # or appropriate lockfile
git commit -m "chore: update lockfile"
git push origin $(git branch --show-current)
```
- Record new commit SHA, loop to poll with `expectedCommitSha`
3. **Otherwise:** Exit with `no_new_cipe` status, providing guidance for user to investigate
## Exit Conditions
Exit the monitoring loop when ANY of these conditions are met:
| Condition | Exit Type |
| ------------------------------------------- | ---------------- |
| CI passes (`cipeStatus == 'SUCCEEDED'`) | Success |
| Max CIPE cycles reached | Timeout |
| Max duration reached | Timeout |
| 3 consecutive no-progress iterations | Circuit breaker |
| No fix available and local fix not possible | Failure |
| No new CIPE and auto-fix not configured | Pre-CIPE failure |
| User cancels | Cancelled |
## Main Loop
### Step 1: Initialize Tracking
```
cycle_count = 0
start_time = now()
no_progress_count = 0
local_verify_count = 0
last_state = null
last_cipe_url = null
expected_commit_sha = null
```
### Step 2: Spawn Subagent
Spawn the `ci-watcher` subagent to poll CI status:
**Fresh start (first spawn, no expected CIPE):**
```
Task(
agent: "ci-watcher",
prompt: "Monitor CI for branch '<branch>'.
Subagent timeout: <subagent-timeout> minutes.
New-CIPE timeout: <new-cipe-timeout> minutes.
Verbosity: <verbosity>."
)
```
**After action that triggers new CIPE (wait mode):**
```
Task(
agent: "ci-watcher",
prompt: "Monitor CI for branch '<branch>'.
Subagent timeout: <subagent-timeout> minutes.
New-CIPE timeout: <new-cipe-timeout> minutes.
Verbosity: <verbosity>.
WAIT MODE: A new CIPE should spawn. Ignore old CIPE until new one appears.
Expected commit SHA: <expected_commit_sha>
Previous CIPE URL: <last_cipe_url>"
)
```
### Step 3: Handle Subagent Response
When subagent returns:
1. Check the returned status
2. Look up default behavior in the table above
3. Check if user instructions override the default
4. Execute the appropriate action
5. **If action expects new CIPE**, update tracking (see Step 3a)
6. If action results in looping, go to Step 2
### Step 3a: Track State for New-CIPE Detection
After actions that should trigger a new CIPE, record state before looping:
| Action | What to Track | Subagent Mode |
| ----------------------------- | --------------------------------------------- | ------------- |
| Fix auto-applying | `last_cipe_url = current cipeUrl` | Wait mode |
| Apply via MCP | `last_cipe_url = current cipeUrl` | Wait mode |
| Apply locally + push | `expected_commit_sha = $(git rev-parse HEAD)` | Wait mode |
| Reject + fix + push | `expected_commit_sha = $(git rev-parse HEAD)` | Wait mode |
| Fix failed + local fix + push | `expected_commit_sha = $(git rev-parse HEAD)` | Wait mode |
| No fix + local fix + push | `expected_commit_sha = $(git rev-parse HEAD)` | Wait mode |
| Environment rerun | `last_cipe_url = current cipeUrl` | Wait mode |
| No-new-CIPE + auto-fix + push | `expected_commit_sha = $(git rev-parse HEAD)` | Wait mode |
**CRITICAL**: When passing `expectedCommitSha` or `last_cipe_url` to the subagent, it enters **wait mode**:
- Subagent will **completely ignore** the old/stale CIPE
- Subagent will only wait for new CIPE to appear
- Subagent will NOT return to main agent with stale CIPE data
- Once new CIPE detected, subagent switches to normal polling
**Why wait mode matters for context preservation**: Stale CIPE data can be very large (task output summaries, suggested fix patches, reasoning). If subagent returns this to main agent, it pollutes main agent's context with useless data since we already processed that CIPE. Wait mode keeps stale data in the subagent, never sending it to main agent.
### Step 4: Progress Tracking
After each action:
- If state changed significantly → reset `no_progress_count = 0`
- If state unchanged → `no_progress_count++`
- On new CI attempt detected → reset `local_verify_count = 0`
## Status Reporting
Based on verbosity level:
| Level | What to Report |
| --------- | -------------------------------------------------------------------------- |
| `minimal` | Only final result (success/failure/timeout) |
| `medium` | State changes + periodic updates ("Cycle N \| Elapsed: Xm \| Status: ...") |
| `verbose` | All of medium + full subagent responses, git outputs, MCP responses |
## User Instruction Examples
Users can override default behaviors:
| Instruction | Effect |
| ------------------------------------------------ | --------------------------------------------- |
| "never auto-apply" | Always prompt before applying any fix |
| "always ask before git push" | Prompt before each push |
| "reject any fix for e2e tasks" | Auto-reject if `failedTaskIds` contains e2e |
| "apply all fixes regardless of verification" | Skip verification check, apply everything |
| "if confidence < 70, reject" | Check confidence field before applying |
| "run 'nx affected -t typecheck' before applying" | Add local verification step |
| "auto-fix workflow failures" | Attempt lockfile updates on pre-CIPE failures |
| "wait 45 min for new CIPE" | Override new-CIPE timeout (default: 10 min) |
## Error Handling
| Error | Action |
| ------------------------ | ------------------------------------------------------------------------------------- |
| Git rebase conflict | Report to user, exit |
| `nx apply-locally` fails | Report to user, attempt manual patch or exit |
| MCP tool error | Retry once, if fails report to user |
| Subagent spawn failure | Retry once, if fails exit with error |
| No new CIPE detected | If `--auto-fix-workflow`, try lockfile update; otherwise report to user with guidance |
| Lockfile auto-fix fails | Report to user, exit with guidance to check CI logs |
## Example Session
### Example 1: Normal Flow with Self-Healing (medium verbosity)
```
[ci-monitor] Starting CI monitor for branch 'feature/add-auth'
[ci-monitor] Config: max-cycles=5, timeout=120m, verbosity=medium
[ci-monitor] Spawning subagent to poll CI status...
[CI Monitor] CI attempt: IN_PROGRESS | Self-Healing: NOT_STARTED | Elapsed: 1m
[CI Monitor] CI attempt: FAILED | Self-Healing: IN_PROGRESS | Elapsed: 3m
[CI Monitor] CI attempt: FAILED | Self-Healing: COMPLETED | Elapsed: 5m
[ci-monitor] Fix available! Verification: COMPLETED
[ci-monitor] Applying fix via MCP...
[ci-monitor] Fix applied in CI. Waiting for new CI attempt...
[ci-monitor] Spawning subagent to poll CI status...
[CI Monitor] New CI attempt detected!
[CI Monitor] CI attempt: SUCCEEDED | Elapsed: 8m
[ci-monitor] CI passed successfully!
[ci-monitor] Summary:
- Total cycles: 2
- Total time: 12m 34s
- Fixes applied: 1
- Result: SUCCESS
```
### Example 2: Pre-CI Failure (medium verbosity)
```
[ci-monitor] Starting CI monitor for branch 'feature/add-products'
[ci-monitor] Config: max-cycles=5, timeout=120m, auto-fix-workflow=true
[ci-monitor] Spawning subagent to poll CI status...
[CI Monitor] CI attempt: FAILED | Self-Healing: COMPLETED | Elapsed: 2m
[ci-monitor] Applying fix locally, enhancing, and pushing...
[ci-monitor] Committed: abc1234
[ci-monitor] Spawning subagent to poll CI status...
[CI Monitor] Waiting for new CI attempt... (expected SHA: abc1234)
[CI Monitor] ⚠️ CI attempt timeout (10 min). Returning no_new_cipe.
[ci-monitor] Status: no_new_cipe
[ci-monitor] --auto-fix-workflow enabled. Attempting lockfile update...
[ci-monitor] Lockfile updated. Committed: def5678
[ci-monitor] Spawning subagent to poll CI status...
[CI Monitor] New CI attempt detected!
[CI Monitor] CI attempt: SUCCEEDED | Elapsed: 18m
[ci-monitor] CI passed successfully!
[ci-monitor] Summary:
- Total cycles: 3
- Total time: 22m 15s
- Fixes applied: 1 (self-healing) + 1 (lockfile)
- Result: SUCCESS
```
+437
View File
@@ -0,0 +1,437 @@
---
name: ci-monitor
description: Monitor Nx Cloud CI pipeline and handle self-healing fixes automatically. Checks for Nx Cloud connection before starting.
---
# CI Monitor Command
You are the orchestrator for monitoring Nx Cloud CI pipeline executions and handling self-healing fixes. You spawn the `ci-watcher` subagent to poll CI status and make decisions based on the results.
## Context
- **Current Branch:** !`git branch --show-current`
- **Current Commit:** !`git rev-parse --short HEAD`
- **Remote Status:** !`git status -sb | head -1`
## User Instructions
$ARGUMENTS
**Important:** If user provides specific instructions, respect them over default behaviors described below.
## Configuration Defaults
| Setting | Default | Description |
| ------------------------- | ------------- | ------------------------------------------------------------------- |
| `--max-cycles` | 10 | Maximum CIPE cycles before timeout |
| `--timeout` | 120 | Maximum duration in minutes |
| `--verbosity` | medium | Output level: minimal, medium, verbose |
| `--branch` | (auto-detect) | Branch to monitor |
| `--subagent-timeout` | 60 | Subagent polling timeout in minutes |
| `--fresh` | false | Ignore previous context, start fresh |
| `--auto-fix-workflow` | false | Attempt common fixes for pre-CIPE failures (e.g., lockfile updates) |
| `--new-cipe-timeout` | 10 | Minutes to wait for new CIPE after action |
| `--local-verify-attempts` | 3 | Max local verification + enhance cycles before pushing to CI |
Parse any overrides from `$ARGUMENTS` and merge with defaults.
## Nx Cloud Connection Check
**CRITICAL**: Before starting the monitoring loop, verify the workspace is connected to Nx Cloud.
### Step 0: Verify Nx Cloud Connection
1. **Check `nx.json`** at workspace root for `nxCloudId` or `nxCloudAccessToken`
2. **If `nx.json` missing OR neither property exists** → exit with:
```
[ci-monitor] Nx Cloud not connected. Unlock 70% faster CI and auto-fix broken PRs with https://nx.dev/nx-cloud
```
3. **If connected** → continue to main loop
## Session Context Behavior
**Important:** Within a Claude Code session, conversation context persists. If you Ctrl+C to interrupt the monitor and re-run `/ci-monitor`, Claude remembers the previous state and may continue from where it left off.
- **To continue monitoring:** Just re-run `/ci-monitor` (context is preserved)
- **To start fresh:** Use `/ci-monitor --fresh` to ignore previous context
- **For a completely clean slate:** Exit Claude Code and restart `claude`
## Default Behaviors by Status
The subagent returns with one of the following statuses. This table defines the **default behavior** for each status. User instructions can override any of these.
| Status | Default Behavior |
| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `ci_success` | Exit with success. Log "CI passed successfully!" |
| `fix_auto_applying` | Fix will be auto-applied by self-healing. Do NOT call MCP. Record `last_cipe_url`, spawn new subagent in wait mode to poll for new CIPE. |
| `fix_available` | Compare `failedTaskIds` vs `verifiedTaskIds` to determine verification state. See **Fix Available Decision Logic** section below. |
| `fix_failed` | Self-healing failed to generate fix. Attempt local fix based on `taskOutputSummary`. If successful → commit, push, loop. If not → exit with failure. |
| `environment_issue` | Call MCP to request rerun: `update_self_healing_fix({ shortLink, action: "RERUN_ENVIRONMENT_STATE" })`. New CIPE spawns automatically. Loop to poll for new CIPE. |
| `no_fix` | CI failed, no fix available (self-healing disabled or not executable). Attempt local fix if possible. Otherwise exit with failure. |
| `no_new_cipe` | Expected CIPE never spawned (CI workflow likely failed before Nx tasks). Report to user, attempt common fixes if configured, or exit with guidance. |
| `polling_timeout` | Subagent polling timeout reached. Exit with timeout. |
| `cipe_canceled` | CIPE was canceled. Exit with canceled status. |
| `cipe_timed_out` | CIPE timed out. Exit with timeout status. |
| `error` | Increment `no_progress_count`. If >= 3 → exit with circuit breaker. Otherwise wait 60s and loop. |
### Fix Available Decision Logic
When subagent returns `fix_available`, main agent compares `failedTaskIds` vs `verifiedTaskIds`:
#### Step 1: Categorize Tasks
1. **Verified tasks** = tasks in both `failedTaskIds` AND `verifiedTaskIds`
2. **Unverified tasks** = tasks in `failedTaskIds` but NOT in `verifiedTaskIds`
3. **E2E tasks** = unverified tasks where target contains "e2e" (task format: `<project>:<target>` or `<project>:<target>:<config>`)
4. **Verifiable tasks** = unverified tasks that are NOT e2e
#### Step 2: Determine Path
| Condition | Path |
| --------------------------------------- | ---------------------------------------- |
| No unverified tasks (all verified) | Apply via MCP |
| Unverified tasks exist, but ALL are e2e | Apply via MCP (treat as verified enough) |
| Verifiable tasks exist | Local verification flow |
#### Step 3a: Apply via MCP (fully/e2e-only verified)
- Call `update_self_healing_fix({ shortLink, action: "APPLY" })`
- Record `last_cipe_url`, spawn subagent in wait mode
#### Step 3b: Local Verification Flow
When verifiable (non-e2e) unverified tasks exist:
1. **Detect package manager:**
- `pnpm-lock.yaml` exists → `pnpm nx`
- `yarn.lock` exists → `yarn nx`
- Otherwise → `npx nx`
2. **Run verifiable tasks in parallel:**
- Spawn `general` subagents to run each task concurrently
- Each subagent runs: `<pm> nx run <taskId>`
- Collect pass/fail results from all subagents
3. **Evaluate results:**
| Result | Action |
| ------------------------- | ---------------------------- |
| ALL verifiable tasks pass | Apply via MCP |
| ANY verifiable task fails | Apply-locally + enhance flow |
4. **Apply-locally + enhance flow:**
- Run `nx apply-locally <shortLink>`
- Enhance the code to fix failing tasks
- Run failing tasks again to verify fix
- If still failing → increment `local_verify_count`, loop back to enhance
- If passing → commit and push, record `expected_commit_sha`, spawn subagent in wait mode
5. **Track attempts** (wraps step 4):
- Increment `local_verify_count` after each enhance cycle
- If `local_verify_count >= local_verify_attempts` (default: 3):
- Get code in commit-able state
- Commit and push with message indicating local verification failed
- Report to user:
```
[ci-monitor] Local verification failed after <N> attempts. Pushed to CI for final validation. Failed: <taskIds>
```
- Record `expected_commit_sha`, spawn subagent in wait mode (let CI be final judge)
#### Commit Message Format
```bash
git commit -m "fix(<projects>): <brief description>
Failed tasks: <taskId1>, <taskId2>
Local verification: passed|enhanced|failed-pushing-to-ci"
```
### Unverified Fix Flow (No Verification Attempted)
When `verificationStatus` is `FAILED`, `NOT_EXECUTABLE`, or fix has `couldAutoApplyTasks != true` with no verification:
- Analyze fix content (`suggestedFix`, `suggestedFixReasoning`, `taskOutputSummary`)
- If fix looks correct → apply via MCP
- If fix needs enhancement → use Apply Locally + Enhance Flow above
- If fix is wrong → reject via MCP, fix from scratch, commit, push
### Auto-Apply Eligibility
The `couldAutoApplyTasks` field indicates whether the fix is eligible for automatic application:
- **`true`**: Fix is eligible for auto-apply. Subagent keeps polling while verification is in progress. Returns `fix_auto_applying` when verified, or `fix_available` if verification fails.
- **`false`** or **`null`**: Fix requires manual action (apply via MCP, apply locally, or reject)
**Key point**: When subagent returns `fix_auto_applying`, do NOT call MCP to apply - self-healing handles it. Just spawn a new subagent in wait mode.
### Apply vs Reject vs Apply Locally
- **Apply via MCP**: Calls `update_self_healing_fix({ shortLink, action: "APPLY" })`. Self-healing agent applies the fix in CI and a new CIPE spawns automatically. No local git operations needed.
- **Apply Locally**: Runs `nx apply-locally <shortLink>`. Applies the patch to your local working directory and sets state to `APPLIED_LOCALLY`. Use this when you want to enhance the fix before pushing.
- **Reject via MCP**: Calls `update_self_healing_fix({ shortLink, action: "REJECT" })`. Marks fix as rejected. Use only when the fix is completely wrong and you'll fix from scratch.
### Apply Locally + Enhance Flow
When the fix needs enhancement (use `nx apply-locally`, NOT reject):
1. Apply the patch locally: `nx apply-locally <shortLink>` (this also updates state to `APPLIED_LOCALLY`)
2. Make additional changes as needed
3. Commit and push:
```bash
git add -A
git commit -m "fix: resolve <failedTaskIds>"
git push origin $(git branch --show-current)
```
4. Loop to poll for new CIPE
### Reject + Fix From Scratch Flow
When the fix is completely wrong:
1. Call MCP to reject: `update_self_healing_fix({ shortLink, action: "REJECT" })`
2. Fix the issue from scratch locally
3. Commit and push:
```bash
git add -A
git commit -m "fix: resolve <failedTaskIds>"
git push origin $(git branch --show-current)
```
4. Loop to poll for new CIPE
### Environment Issue Handling
When `failureClassification == 'ENVIRONMENT_STATE'`:
1. Call MCP to request rerun: `update_self_healing_fix({ shortLink, action: "RERUN_ENVIRONMENT_STATE" })`
2. New CIPE spawns automatically (no local git operations needed)
3. Loop to poll for new CIPE with `previousCipeUrl` set
### No-New-CIPE Handling
When `status == 'no_new_cipe'`:
This means the expected CIPE was never created - CI likely failed before Nx tasks could run.
1. **Report to user:**
```
[ci-monitor] No CI attempt for <sha> after 10 min. Check CI provider for pre-Nx failures (install, checkout, auth). Last CI attempt: <previousCipeUrl>
```
2. **If user configured auto-fix attempts** (e.g., `--auto-fix-workflow`):
- Detect package manager: check for `pnpm-lock.yaml`, `yarn.lock`, `package-lock.json`
- Run install to update lockfile:
```bash
pnpm install # or npm install / yarn install
```
- If lockfile changed:
```bash
git add pnpm-lock.yaml # or appropriate lockfile
git commit -m "chore: update lockfile"
git push origin $(git branch --show-current)
```
- Record new commit SHA, loop to poll with `expectedCommitSha`
3. **Otherwise:** Exit with `no_new_cipe` status, providing guidance for user to investigate
## Exit Conditions
Exit the monitoring loop when ANY of these conditions are met:
| Condition | Exit Type |
| ------------------------------------------- | ---------------- |
| CI passes (`cipeStatus == 'SUCCEEDED'`) | Success |
| Max CIPE cycles reached | Timeout |
| Max duration reached | Timeout |
| 3 consecutive no-progress iterations | Circuit breaker |
| No fix available and local fix not possible | Failure |
| No new CIPE and auto-fix not configured | Pre-CIPE failure |
| User cancels | Cancelled |
## Main Loop
### Step 1: Initialize Tracking
```
cycle_count = 0
start_time = now()
no_progress_count = 0
local_verify_count = 0
last_state = null
last_cipe_url = null
expected_commit_sha = null
```
### Step 2: Spawn Subagent
Spawn the `ci-watcher` subagent to poll CI status:
**Fresh start (first spawn, no expected CIPE):**
```
Task(
agent: "ci-watcher",
prompt: "Monitor CI for branch '<branch>'.
Subagent timeout: <subagent-timeout> minutes.
New-CIPE timeout: <new-cipe-timeout> minutes.
Verbosity: <verbosity>."
)
```
**After action that triggers new CIPE (wait mode):**
```
Task(
agent: "ci-watcher",
prompt: "Monitor CI for branch '<branch>'.
Subagent timeout: <subagent-timeout> minutes.
New-CIPE timeout: <new-cipe-timeout> minutes.
Verbosity: <verbosity>.
WAIT MODE: A new CIPE should spawn. Ignore old CIPE until new one appears.
Expected commit SHA: <expected_commit_sha>
Previous CIPE URL: <last_cipe_url>"
)
```
### Step 3: Handle Subagent Response
When subagent returns:
1. Check the returned status
2. Look up default behavior in the table above
3. Check if user instructions override the default
4. Execute the appropriate action
5. **If action expects new CIPE**, update tracking (see Step 3a)
6. If action results in looping, go to Step 2
### Step 3a: Track State for New-CIPE Detection
After actions that should trigger a new CIPE, record state before looping:
| Action | What to Track | Subagent Mode |
| ----------------------------- | --------------------------------------------- | ------------- |
| Fix auto-applying | `last_cipe_url = current cipeUrl` | Wait mode |
| Apply via MCP | `last_cipe_url = current cipeUrl` | Wait mode |
| Apply locally + push | `expected_commit_sha = $(git rev-parse HEAD)` | Wait mode |
| Reject + fix + push | `expected_commit_sha = $(git rev-parse HEAD)` | Wait mode |
| Fix failed + local fix + push | `expected_commit_sha = $(git rev-parse HEAD)` | Wait mode |
| No fix + local fix + push | `expected_commit_sha = $(git rev-parse HEAD)` | Wait mode |
| Environment rerun | `last_cipe_url = current cipeUrl` | Wait mode |
| No-new-CIPE + auto-fix + push | `expected_commit_sha = $(git rev-parse HEAD)` | Wait mode |
**CRITICAL**: When passing `expectedCommitSha` or `last_cipe_url` to the subagent, it enters **wait mode**:
- Subagent will **completely ignore** the old/stale CIPE
- Subagent will only wait for new CIPE to appear
- Subagent will NOT return to main agent with stale CIPE data
- Once new CIPE detected, subagent switches to normal polling
**Why wait mode matters for context preservation**: Stale CIPE data can be very large (task output summaries, suggested fix patches, reasoning). If subagent returns this to main agent, it pollutes main agent's context with useless data since we already processed that CIPE. Wait mode keeps stale data in the subagent, never sending it to main agent.
### Step 4: Progress Tracking
After each action:
- If state changed significantly → reset `no_progress_count = 0`
- If state unchanged → `no_progress_count++`
- On new CI attempt detected → reset `local_verify_count = 0`
## Status Reporting
Based on verbosity level:
| Level | What to Report |
| --------- | -------------------------------------------------------------------------- |
| `minimal` | Only final result (success/failure/timeout) |
| `medium` | State changes + periodic updates ("Cycle N \| Elapsed: Xm \| Status: ...") |
| `verbose` | All of medium + full subagent responses, git outputs, MCP responses |
## User Instruction Examples
Users can override default behaviors:
| Instruction | Effect |
| ------------------------------------------------ | --------------------------------------------- |
| "never auto-apply" | Always prompt before applying any fix |
| "always ask before git push" | Prompt before each push |
| "reject any fix for e2e tasks" | Auto-reject if `failedTaskIds` contains e2e |
| "apply all fixes regardless of verification" | Skip verification check, apply everything |
| "if confidence < 70, reject" | Check confidence field before applying |
| "run 'nx affected -t typecheck' before applying" | Add local verification step |
| "auto-fix workflow failures" | Attempt lockfile updates on pre-CIPE failures |
| "wait 45 min for new CIPE" | Override new-CIPE timeout (default: 10 min) |
## Error Handling
| Error | Action |
| ------------------------ | ------------------------------------------------------------------------------------- |
| Git rebase conflict | Report to user, exit |
| `nx apply-locally` fails | Report to user, attempt manual patch or exit |
| MCP tool error | Retry once, if fails report to user |
| Subagent spawn failure | Retry once, if fails exit with error |
| No new CIPE detected | If `--auto-fix-workflow`, try lockfile update; otherwise report to user with guidance |
| Lockfile auto-fix fails | Report to user, exit with guidance to check CI logs |
## Example Session
### Example 1: Normal Flow with Self-Healing (medium verbosity)
```
[ci-monitor] Starting CI monitor for branch 'feature/add-auth'
[ci-monitor] Config: max-cycles=5, timeout=120m, verbosity=medium
[ci-monitor] Spawning subagent to poll CI status...
[CI Monitor] CI attempt: IN_PROGRESS | Self-Healing: NOT_STARTED | Elapsed: 1m
[CI Monitor] CI attempt: FAILED | Self-Healing: IN_PROGRESS | Elapsed: 3m
[CI Monitor] CI attempt: FAILED | Self-Healing: COMPLETED | Elapsed: 5m
[ci-monitor] Fix available! Verification: COMPLETED
[ci-monitor] Applying fix via MCP...
[ci-monitor] Fix applied in CI. Waiting for new CI attempt...
[ci-monitor] Spawning subagent to poll CI status...
[CI Monitor] New CI attempt detected!
[CI Monitor] CI attempt: SUCCEEDED | Elapsed: 8m
[ci-monitor] CI passed successfully!
[ci-monitor] Summary:
- Total cycles: 2
- Total time: 12m 34s
- Fixes applied: 1
- Result: SUCCESS
```
### Example 2: Pre-CI Failure (medium verbosity)
```
[ci-monitor] Starting CI monitor for branch 'feature/add-products'
[ci-monitor] Config: max-cycles=5, timeout=120m, auto-fix-workflow=true
[ci-monitor] Spawning subagent to poll CI status...
[CI Monitor] CI attempt: FAILED | Self-Healing: COMPLETED | Elapsed: 2m
[ci-monitor] Applying fix locally, enhancing, and pushing...
[ci-monitor] Committed: abc1234
[ci-monitor] Spawning subagent to poll CI status...
[CI Monitor] Waiting for new CI attempt... (expected SHA: abc1234)
[CI Monitor] ⚠️ CI attempt timeout (10 min). Returning no_new_cipe.
[ci-monitor] Status: no_new_cipe
[ci-monitor] --auto-fix-workflow enabled. Attempting lockfile update...
[ci-monitor] Lockfile updated. Committed: def5678
[ci-monitor] Spawning subagent to poll CI status...
[CI Monitor] New CI attempt detected!
[CI Monitor] CI attempt: SUCCEEDED | Elapsed: 18m
[ci-monitor] CI passed successfully!
[ci-monitor] Summary:
- Total cycles: 3
- Total time: 22m 15s
- Fixes applied: 1 (self-healing) + 1 (lockfile)
- Result: SUCCESS
```
+228
View File
@@ -0,0 +1,228 @@
---
name: nx-generate
description: Generate code using nx generators. USE WHEN scaffolding code or transforming existing code - for example creating libraries or applications, or anything else that is boilerplate code or automates repetitive tasks. ALWAYS use this first when generating code with Nx instead of calling MCP tools or running nx generate immediately.
---
# Run Nx Generator
Nx generators are powerful tools that scaffold projects, make automated code migrations or automate repetitive tasks in a monorepo. They ensure consistency across the codebase and reduce boilerplate work.
This skill applies when the user wants to:
- Create new projects like libraries or applications
- Scaffold features or boilerplate code
- Run workspace-specific or custom generators
- Do anything else that an nx generator exists for
## Generator Discovery Flow
### Step 1: List Available Generators
Use the Nx CLI to discover available generators:
- List all generators for a plugin: `npx nx list @nx/react`
- View available plugins: `npx nx list`
This includes:
- Plugin generators (e.g., `@nx/react:library`, `@nx/js:library`)
- Local workspace generators (defined in the repo's own plugins)
### Step 2: Match Generator to User Request
Based on the user's request, identify which generator(s) could fulfill their needs. Consider:
- What artifact type they want to create (library, application, etc.)
- Which framework or technology stack is relevant
- Whether they mentioned specific generator names
**IMPORTANT**: When both a local workspace generator and an external plugin generator could satisfy the request, **always prefer the local workspace generator**. Local generators are customized for the specific repo's patterns and conventions.
It's possible that the user request is something that no Nx generator exists for whatsoever. In this case, you can stop using this skill and try to help the user another way. HOWEVER, the burden of proof for this is high. Before aborting, carefully consider each and every generator that's available. Look into details for any that could be related in any way before making this decision.
## Pre-Execution Checklist
Before running any generator, complete these steps:
### 1. Fetch Generator Schema
Use the `--help` flag to understand all available options:
```bash
npx nx g @nx/react:library --help
```
Pay attention to:
- Required options that must be provided
- Optional options that may be relevant to the user's request
- Default values that might need to be overridden
### 2. Read Generator Source Code
Understanding what the generator actually does helps you:
- Know what files will be created/modified
- Understand any side effects (updating configs, installing deps, etc.)
- Identify options that might not be obvious from the schema
To find generator source code:
- For plugin generators: Use `node -e "console.log(require.resolve('@nx/<plugin>/generators.json'));"` to find the generators.json, then locate the source from there
- If that fails, read directly from `node_modules/<plugin>/generators.json`
- For local generators: They are typically in `tools/generators/` or a local plugin directory. You can search the repo for the generator name to find it.
### 2.5 Reevaluate if the generator is right
Once you have built up an understanding of what the selected generator does, reconsider: Is this the right generator to service the user request?
If not, it's okay to go back to the Generator Discovery Flow and select a different generator before proceeding. If you do, make sure to go through the entire pre-execution checklist once more.
### 3. Understand Repo Context
Before generating, examine the target area of the codebase:
- Look at similar existing artifacts (other libraries, applications, etc.)
- Identify patterns and conventions used in the repo
- Note naming conventions, file structures, and configuration patterns
- Try to match these patterns when configuring the generator
For example, if similar libraries are using a specific test runner, build tool or linter, try to match that if possible.
If projects or other artifacts are organized with a specific naming convention, try to match it.
### 4. Validate Required Options
Ensure all required options have values:
- Map the user's request to generator options
- Infer values from context where possible
- Ask the user for any critical missing information
## Execution
Keep in mind that you might have to prefix things with npx/pnpx/yarn if the user doesn't have nx installed globally.
Many generators will behave differently based on where they are executed. For example, first-party nx library generators use the cwd to determine the directory that the library should be placed in. This is highly important.
### Consider Dry-Run (Optional)
Running with `--dry-run` first is strongly encouraged but not mandatory. Use your judgment:
- For complex generators or unfamiliar territory: do a dry-run first
- For simple, well-understood generators: may proceed directly
- Dry-run shows file names and created/deleted/modified markers, but not content
- There are cases where a generator does not support dry-run (for example if it had to install an npm package) - in that case --dry-run might fail. Don't be discouraged but simply move on to running the generator for real and iterating from there.
### Running the Generator
Execute the generator with:
```bash
nx generate <generator-name> <options> --no-interactive
```
**CRITICAL**: Always include `--no-interactive` to prevent prompts that would hang the execution.
Example:
```bash
nx generate @nx/react:library --name=my-utils --no-interactive
```
### Handling Generator Failures
If the generator fails:
1. **Diagnose the error** - Read the error message carefully
2. **Identify the cause** - Missing options, invalid values, conflicts, etc.
3. **Attempt automatic fix** - Adjust options or resolve conflicts
4. **Retry** - Run the generator again with corrected options
Common failure reasons:
- Missing required options
- Invalid option values
- Conflicting with existing files
- Missing dependencies
- Generator doesn't support certain flag combinations
## Post-Generation
### 1. Modify Generated Code (If Needed)
Generators provide a starting point, but the output may need adjustment to match the user's specific requirements:
- Add or modify functionality as requested
- Adjust imports, exports, or configurations
- Integrate with existing code patterns in the repo
### 2. Format Code
Run formatting on all generated/modified files:
```bash
nx format --fix
```
Languages other than javascript/typescript might need other formatting invocations too.
### 3. Run Verification
Verify that the generated code works correctly. What this looks like will vary depending on the type of generator and the targets available.
If the generator created a new project, run its targets directly
Use your best judgement to determine what needs to be verified.
Example:
```bash
nx lint <new-project>
nx test <new-project>
nx build <new-project>
```
### 4. Handle Verification Failures
When verification fails:
**If scope is manageable** (a few lint errors, minor type issues):
- Fix the issues
- Re-run verification to confirm
**If issues are extensive** (many errors, complex problems):
- Attempt simple, obvious fixes first
- If still failing, escalate to the user with:
- Description of what was generated
- What verification is failing
- What you've attempted to fix
- Remaining issues that need user input
## Error Handling
### Generator Failures
- Check the error message for specific causes
- Verify all required options are provided
- Check for conflicts with existing files
- Ensure the generator name and options are correct
### Missing Options
- Consult the generator schema for required fields
- Infer values from context when reasonable
- Ask the user for values that cannot be inferred
## Key Principles
1. **Local generators first** - Always prefer workspace/local generators over external plugin generators when both could work
2. **Understand before running** - Read both the schema AND the source code to fully understand what will happen
3. **No prompts** - Always use `--no-interactive` to prevent hanging
4. **Generators are starting points** - Modify the output as needed to fully satisfy the user's requirements
5. **Verify changes work** - Don't just generate; ensure the code builds, lints, and tests pass
6. **Be proactive about fixes** - Don't just report errors; attempt to resolve them automatically when possible
7. **Match repo patterns** - Study existing similar code in the repo and match its conventions
+9
View File
@@ -0,0 +1,9 @@
---
name: nx-plugins
description: Find and add Nx plugins. USE WHEN user wants to discover available plugins, install a new plugin, or add support for a specific framework or technology to the workspace.
---
## Finding and Installing new plugins
- List plugins: `pnpm nx list`
- Install plugins `pnpm nx add <plugin>`. Example: `pnpm nx add @nx/react`.
+58
View File
@@ -0,0 +1,58 @@
---
name: nx-run-tasks
description: Helps with running tasks in an Nx workspace. USE WHEN the user wants to execute build, test, lint, serve, or run any other tasks defined in the workspace.
---
You can run tasks with Nx in the following way.
Keep in mind that you might have to prefix things with npx/pnpx/yarn if the user doesn't have nx installed globally. Look at the package.json or lockfile to determine which package manager is in use.
For more details on any command, run it with `--help` (e.g. `nx run-many --help`, `nx affected --help`).
## Understand which tasks can be run
You can check those via `nx show project <projectname> --json`, for example `nx show project myapp --json`. It contains a `targets` section which has information about targets that can be run. You can also just look at the `package.json` scripts or `project.json` targets, but you might miss out on inferred tasks by Nx plugins.
## Run a single task
```
nx run <project>:<task>
```
where `project` is the project name defined in `package.json` or `project.json` (if present).
## Run multiple tasks
```
nx run-many -t build test lint typecheck
```
You can pass a `-p` flag to filter to specific projects, otherwise it runs on all projects. You can also use `--exclude` to exclude projects, and `--parallel` to control the number of parallel processes (default is 3).
Examples:
- `nx run-many -t test -p proj1 proj2` — test specific projects
- `nx run-many -t test --projects=*-app --exclude=excluded-app` — test projects matching a pattern
- `nx run-many -t test --projects=tag:api-*` — test projects by tag
## Run tasks for affected projects
Use `nx affected` to only run tasks on projects that have been changed and projects that depend on changed projects. This is especially useful in CI and for large workspaces.
```
nx affected -t build test lint
```
By default it compares against the base branch. You can customize this:
- `nx affected -t test --base=main --head=HEAD` — compare against a specific base and head
- `nx affected -t test --files=libs/mylib/src/index.ts` — specify changed files directly
## Useful flags
These flags work with `run`, `run-many`, and `affected`:
- `--skipNxCache` — rerun tasks even when results are cached
- `--verbose` — print additional information such as stack traces
- `--nxBail` — stop execution after the first failed task
- `--configuration=<name>` — use a specific configuration (e.g. `production`)
+186
View File
@@ -0,0 +1,186 @@
---
name: nx-workspace
description: "Explore and understand Nx workspaces. USE WHEN answering any questions about the nx workspace, the projects in it or tasks to run. EXAMPLES: 'What projects are in this workspace?', 'How is project X configured?', 'What targets can I run?', 'What's affected by my changes?', 'Which projects depend on library Y?', or any questions about Nx workspace structure, project configuration, or available tasks."
---
# Nx Workspace Exploration
This skill provides read-only exploration of Nx workspaces. Use it to understand workspace structure, project configuration, available targets, and dependencies.
Keep in mind that you might have to prefix commands with `npx`/`pnpx`/`yarn` if nx isn't installed globally. Check the lockfile to determine the package manager in use.
## Listing Projects
Use `nx show projects` to list projects in the workspace.
```bash
# List all projects
nx show projects
# Filter by pattern (glob)
nx show projects --projects "apps/*"
nx show projects --projects "shared-*"
# Filter by project type
nx show projects --type app
nx show projects --type lib
nx show projects --type e2e
# Filter by target (projects that have a specific target)
nx show projects --withTarget build
nx show projects --withTarget e2e
# Find affected projects (changed since base branch)
nx show projects --affected
nx show projects --affected --base=main
nx show projects --affected --type app
# Combine filters
nx show projects --type lib --withTarget test
nx show projects --affected --exclude="*-e2e"
# Output as JSON
nx show projects --json
```
## Project Configuration
Use `nx show project <name> --json` to get the full resolved configuration for a project.
**Important**: Do NOT read `project.json` directly - it only contains partial configuration. The `nx show project` command returns the full resolved config including inferred targets from plugins.
You can read the full project schema at `node_modules/nx/schemas/project-schema.json` to understand nx project configuration options.
```bash
# Get full project configuration
nx show project my-app --json
# Extract specific parts from the JSON
nx show project my-app --json | jq '.targets'
nx show project my-app --json | jq '.targets.build'
nx show project my-app --json | jq '.targets | keys'
# Check project metadata
nx show project my-app --json | jq '{name, root, sourceRoot, projectType, tags}'
```
## Target Information
Targets define what tasks can be run on a project.
```bash
# List all targets for a project
nx show project my-app --json | jq '.targets | keys'
# Get full target configuration
nx show project my-app --json | jq '.targets.build'
# Check target executor/command
nx show project my-app --json | jq '.targets.build.executor'
nx show project my-app --json | jq '.targets.build.command'
# View target options
nx show project my-app --json | jq '.targets.build.options'
# Check target inputs/outputs (for caching)
nx show project my-app --json | jq '.targets.build.inputs'
nx show project my-app --json | jq '.targets.build.outputs'
# Find projects with a specific target
nx show projects --withTarget serve
nx show projects --withTarget e2e
```
## Workspace Configuration
Read `nx.json` directly for workspace-level configuration.
You can read the full project schema at `node_modules/nx/schemas/nx-schema.json` to understand nx project configuration options.
```bash
# Read the full nx.json
cat nx.json
# Or use jq for specific sections
cat nx.json | jq '.targetDefaults'
cat nx.json | jq '.namedInputs'
cat nx.json | jq '.plugins'
cat nx.json | jq '.generators'
```
Key nx.json sections:
- `targetDefaults` - Default configuration applied to all targets of a given name
- `namedInputs` - Reusable input definitions for caching
- `plugins` - Nx plugins and their configuration
- ...and much more, read the schema or nx.json for details
## Affected Projects
Find projects affected by changes in the current branch.
```bash
# Affected since base branch (auto-detected)
nx show projects --affected
# Affected with explicit base
nx show projects --affected --base=main
nx show projects --affected --base=origin/main
# Affected between two commits
nx show projects --affected --base=abc123 --head=def456
# Affected apps only
nx show projects --affected --type app
# Affected excluding e2e projects
nx show projects --affected --exclude="*-e2e"
# Affected by uncommitted changes
nx show projects --affected --uncommitted
# Affected by untracked files
nx show projects --affected --untracked
```
## Common Exploration Patterns
### "What's in this workspace?"
```bash
nx show projects
nx show projects --type app
nx show projects --type lib
```
### "How do I build/test/lint project X?"
```bash
nx show project X --json | jq '.targets | keys'
nx show project X --json | jq '.targets.build'
```
### "What depends on library Y?"
```bash
# Find projects that may depend on Y by searching for imports
# (Nx doesn't have a direct "dependents" command via CLI)
grep -r "from '@myorg/Y'" --include="*.ts" --include="*.tsx" apps/ libs/
```
### "What configuration options are available?"
```bash
cat node_modules/nx/schemas/nx-schema.json | jq '.properties | keys'
cat node_modules/nx/schemas/project-schema.json | jq '.properties | keys'
```
### "Why is project X affected?"
```bash
# Check what files changed
git diff --name-only main
# See which project owns those files
nx show project X --json | jq '.root'
```
+438
View File
@@ -0,0 +1,438 @@
description = "Monitor Nx Cloud CI pipeline and handle self-healing fixes automatically. Checks for Nx Cloud connection before starting."
prompt = """
# CI Monitor Command
You are the orchestrator for monitoring Nx Cloud CI pipeline executions and handling self-healing fixes. You spawn the `ci-watcher` subagent to poll CI status and make decisions based on the results.
## Context
- **Current Branch:** !`git branch --show-current`
- **Current Commit:** !`git rev-parse --short HEAD`
- **Remote Status:** !`git status -sb | head -1`
## User Instructions
{{args}}
**Important:** If user provides specific instructions, respect them over default behaviors described below.
## Configuration Defaults
| Setting | Default | Description |
| ------------------------- | ------------- | ------------------------------------------------------------------- |
| `--max-cycles` | 10 | Maximum CIPE cycles before timeout |
| `--timeout` | 120 | Maximum duration in minutes |
| `--verbosity` | medium | Output level: minimal, medium, verbose |
| `--branch` | (auto-detect) | Branch to monitor |
| `--subagent-timeout` | 60 | Subagent polling timeout in minutes |
| `--fresh` | false | Ignore previous context, start fresh |
| `--auto-fix-workflow` | false | Attempt common fixes for pre-CIPE failures (e.g., lockfile updates) |
| `--new-cipe-timeout` | 10 | Minutes to wait for new CIPE after action |
| `--local-verify-attempts` | 3 | Max local verification + enhance cycles before pushing to CI |
Parse any overrides from `{{args}}` and merge with defaults.
## Nx Cloud Connection Check
**CRITICAL**: Before starting the monitoring loop, verify the workspace is connected to Nx Cloud.
### Step 0: Verify Nx Cloud Connection
1. **Check `nx.json`** at workspace root for `nxCloudId` or `nxCloudAccessToken`
2. **If `nx.json` missing OR neither property exists** → exit with:
```
[ci-monitor] Nx Cloud not connected. Unlock 70% faster CI and auto-fix broken PRs with https://nx.dev/nx-cloud
```
3. **If connected** → continue to main loop
## Session Context Behavior
**Important:** Within a Claude Code session, conversation context persists. If you Ctrl+C to interrupt the monitor and re-run `/ci-monitor`, Claude remembers the previous state and may continue from where it left off.
- **To continue monitoring:** Just re-run `/ci-monitor` (context is preserved)
- **To start fresh:** Use `/ci-monitor --fresh` to ignore previous context
- **For a completely clean slate:** Exit Claude Code and restart `claude`
## Default Behaviors by Status
The subagent returns with one of the following statuses. This table defines the **default behavior** for each status. User instructions can override any of these.
| Status | Default Behavior |
| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `ci_success` | Exit with success. Log "CI passed successfully!" |
| `fix_auto_applying` | Fix will be auto-applied by self-healing. Do NOT call MCP. Record `last_cipe_url`, spawn new subagent in wait mode to poll for new CIPE. |
| `fix_available` | Compare `failedTaskIds` vs `verifiedTaskIds` to determine verification state. See **Fix Available Decision Logic** section below. |
| `fix_failed` | Self-healing failed to generate fix. Attempt local fix based on `taskOutputSummary`. If successful → commit, push, loop. If not → exit with failure. |
| `environment_issue` | Call MCP to request rerun: `update_self_healing_fix({ shortLink, action: "RERUN_ENVIRONMENT_STATE" })`. New CIPE spawns automatically. Loop to poll for new CIPE. |
| `no_fix` | CI failed, no fix available (self-healing disabled or not executable). Attempt local fix if possible. Otherwise exit with failure. |
| `no_new_cipe` | Expected CIPE never spawned (CI workflow likely failed before Nx tasks). Report to user, attempt common fixes if configured, or exit with guidance. |
| `polling_timeout` | Subagent polling timeout reached. Exit with timeout. |
| `cipe_canceled` | CIPE was canceled. Exit with canceled status. |
| `cipe_timed_out` | CIPE timed out. Exit with timeout status. |
| `error` | Increment `no_progress_count`. If >= 3 → exit with circuit breaker. Otherwise wait 60s and loop. |
### Fix Available Decision Logic
When subagent returns `fix_available`, main agent compares `failedTaskIds` vs `verifiedTaskIds`:
#### Step 1: Categorize Tasks
1. **Verified tasks** = tasks in both `failedTaskIds` AND `verifiedTaskIds`
2. **Unverified tasks** = tasks in `failedTaskIds` but NOT in `verifiedTaskIds`
3. **E2E tasks** = unverified tasks where target contains "e2e" (task format: `<project>:<target>` or `<project>:<target>:<config>`)
4. **Verifiable tasks** = unverified tasks that are NOT e2e
#### Step 2: Determine Path
| Condition | Path |
| --------------------------------------- | ---------------------------------------- |
| No unverified tasks (all verified) | Apply via MCP |
| Unverified tasks exist, but ALL are e2e | Apply via MCP (treat as verified enough) |
| Verifiable tasks exist | Local verification flow |
#### Step 3a: Apply via MCP (fully/e2e-only verified)
- Call `update_self_healing_fix({ shortLink, action: "APPLY" })`
- Record `last_cipe_url`, spawn subagent in wait mode
#### Step 3b: Local Verification Flow
When verifiable (non-e2e) unverified tasks exist:
1. **Detect package manager:**
- `pnpm-lock.yaml` exists → `pnpm nx`
- `yarn.lock` exists → `yarn nx`
- Otherwise → `npx nx`
2. **Run verifiable tasks in parallel:**
- Spawn `general` subagents to run each task concurrently
- Each subagent runs: `<pm> nx run <taskId>`
- Collect pass/fail results from all subagents
3. **Evaluate results:**
| Result | Action |
| ------------------------- | ---------------------------- |
| ALL verifiable tasks pass | Apply via MCP |
| ANY verifiable task fails | Apply-locally + enhance flow |
4. **Apply-locally + enhance flow:**
- Run `nx apply-locally <shortLink>`
- Enhance the code to fix failing tasks
- Run failing tasks again to verify fix
- If still failing → increment `local_verify_count`, loop back to enhance
- If passing → commit and push, record `expected_commit_sha`, spawn subagent in wait mode
5. **Track attempts** (wraps step 4):
- Increment `local_verify_count` after each enhance cycle
- If `local_verify_count >= local_verify_attempts` (default: 3):
- Get code in commit-able state
- Commit and push with message indicating local verification failed
- Report to user:
```
[ci-monitor] Local verification failed after <N> attempts. Pushed to CI for final validation. Failed: <taskIds>
```
- Record `expected_commit_sha`, spawn subagent in wait mode (let CI be final judge)
#### Commit Message Format
```bash
git commit -m "fix(<projects>): <brief description>
Failed tasks: <taskId1>, <taskId2>
Local verification: passed|enhanced|failed-pushing-to-ci"
```
### Unverified Fix Flow (No Verification Attempted)
When `verificationStatus` is `FAILED`, `NOT_EXECUTABLE`, or fix has `couldAutoApplyTasks != true` with no verification:
- Analyze fix content (`suggestedFix`, `suggestedFixReasoning`, `taskOutputSummary`)
- If fix looks correct → apply via MCP
- If fix needs enhancement → use Apply Locally + Enhance Flow above
- If fix is wrong → reject via MCP, fix from scratch, commit, push
### Auto-Apply Eligibility
The `couldAutoApplyTasks` field indicates whether the fix is eligible for automatic application:
- **`true`**: Fix is eligible for auto-apply. Subagent keeps polling while verification is in progress. Returns `fix_auto_applying` when verified, or `fix_available` if verification fails.
- **`false`** or **`null`**: Fix requires manual action (apply via MCP, apply locally, or reject)
**Key point**: When subagent returns `fix_auto_applying`, do NOT call MCP to apply - self-healing handles it. Just spawn a new subagent in wait mode.
### Apply vs Reject vs Apply Locally
- **Apply via MCP**: Calls `update_self_healing_fix({ shortLink, action: "APPLY" })`. Self-healing agent applies the fix in CI and a new CIPE spawns automatically. No local git operations needed.
- **Apply Locally**: Runs `nx apply-locally <shortLink>`. Applies the patch to your local working directory and sets state to `APPLIED_LOCALLY`. Use this when you want to enhance the fix before pushing.
- **Reject via MCP**: Calls `update_self_healing_fix({ shortLink, action: "REJECT" })`. Marks fix as rejected. Use only when the fix is completely wrong and you'll fix from scratch.
### Apply Locally + Enhance Flow
When the fix needs enhancement (use `nx apply-locally`, NOT reject):
1. Apply the patch locally: `nx apply-locally <shortLink>` (this also updates state to `APPLIED_LOCALLY`)
2. Make additional changes as needed
3. Commit and push:
```bash
git add -A
git commit -m "fix: resolve <failedTaskIds>"
git push origin $(git branch --show-current)
```
4. Loop to poll for new CIPE
### Reject + Fix From Scratch Flow
When the fix is completely wrong:
1. Call MCP to reject: `update_self_healing_fix({ shortLink, action: "REJECT" })`
2. Fix the issue from scratch locally
3. Commit and push:
```bash
git add -A
git commit -m "fix: resolve <failedTaskIds>"
git push origin $(git branch --show-current)
```
4. Loop to poll for new CIPE
### Environment Issue Handling
When `failureClassification == 'ENVIRONMENT_STATE'`:
1. Call MCP to request rerun: `update_self_healing_fix({ shortLink, action: "RERUN_ENVIRONMENT_STATE" })`
2. New CIPE spawns automatically (no local git operations needed)
3. Loop to poll for new CIPE with `previousCipeUrl` set
### No-New-CIPE Handling
When `status == 'no_new_cipe'`:
This means the expected CIPE was never created - CI likely failed before Nx tasks could run.
1. **Report to user:**
```
[ci-monitor] No CI attempt for <sha> after 10 min. Check CI provider for pre-Nx failures (install, checkout, auth). Last CI attempt: <previousCipeUrl>
```
2. **If user configured auto-fix attempts** (e.g., `--auto-fix-workflow`):
- Detect package manager: check for `pnpm-lock.yaml`, `yarn.lock`, `package-lock.json`
- Run install to update lockfile:
```bash
pnpm install # or npm install / yarn install
```
- If lockfile changed:
```bash
git add pnpm-lock.yaml # or appropriate lockfile
git commit -m "chore: update lockfile"
git push origin $(git branch --show-current)
```
- Record new commit SHA, loop to poll with `expectedCommitSha`
3. **Otherwise:** Exit with `no_new_cipe` status, providing guidance for user to investigate
## Exit Conditions
Exit the monitoring loop when ANY of these conditions are met:
| Condition | Exit Type |
| ------------------------------------------- | ---------------- |
| CI passes (`cipeStatus == 'SUCCEEDED'`) | Success |
| Max CIPE cycles reached | Timeout |
| Max duration reached | Timeout |
| 3 consecutive no-progress iterations | Circuit breaker |
| No fix available and local fix not possible | Failure |
| No new CIPE and auto-fix not configured | Pre-CIPE failure |
| User cancels | Cancelled |
## Main Loop
### Step 1: Initialize Tracking
```
cycle_count = 0
start_time = now()
no_progress_count = 0
local_verify_count = 0
last_state = null
last_cipe_url = null
expected_commit_sha = null
```
### Step 2: Spawn Subagent
Spawn the `ci-watcher` subagent to poll CI status:
**Fresh start (first spawn, no expected CIPE):**
```
Task(
agent: "ci-watcher",
prompt: "Monitor CI for branch '<branch>'.
Subagent timeout: <subagent-timeout> minutes.
New-CIPE timeout: <new-cipe-timeout> minutes.
Verbosity: <verbosity>."
)
```
**After action that triggers new CIPE (wait mode):**
```
Task(
agent: "ci-watcher",
prompt: "Monitor CI for branch '<branch>'.
Subagent timeout: <subagent-timeout> minutes.
New-CIPE timeout: <new-cipe-timeout> minutes.
Verbosity: <verbosity>.
WAIT MODE: A new CIPE should spawn. Ignore old CIPE until new one appears.
Expected commit SHA: <expected_commit_sha>
Previous CIPE URL: <last_cipe_url>"
)
```
### Step 3: Handle Subagent Response
When subagent returns:
1. Check the returned status
2. Look up default behavior in the table above
3. Check if user instructions override the default
4. Execute the appropriate action
5. **If action expects new CIPE**, update tracking (see Step 3a)
6. If action results in looping, go to Step 2
### Step 3a: Track State for New-CIPE Detection
After actions that should trigger a new CIPE, record state before looping:
| Action | What to Track | Subagent Mode |
| ----------------------------- | --------------------------------------------- | ------------- |
| Fix auto-applying | `last_cipe_url = current cipeUrl` | Wait mode |
| Apply via MCP | `last_cipe_url = current cipeUrl` | Wait mode |
| Apply locally + push | `expected_commit_sha = $(git rev-parse HEAD)` | Wait mode |
| Reject + fix + push | `expected_commit_sha = $(git rev-parse HEAD)` | Wait mode |
| Fix failed + local fix + push | `expected_commit_sha = $(git rev-parse HEAD)` | Wait mode |
| No fix + local fix + push | `expected_commit_sha = $(git rev-parse HEAD)` | Wait mode |
| Environment rerun | `last_cipe_url = current cipeUrl` | Wait mode |
| No-new-CIPE + auto-fix + push | `expected_commit_sha = $(git rev-parse HEAD)` | Wait mode |
**CRITICAL**: When passing `expectedCommitSha` or `last_cipe_url` to the subagent, it enters **wait mode**:
- Subagent will **completely ignore** the old/stale CIPE
- Subagent will only wait for new CIPE to appear
- Subagent will NOT return to main agent with stale CIPE data
- Once new CIPE detected, subagent switches to normal polling
**Why wait mode matters for context preservation**: Stale CIPE data can be very large (task output summaries, suggested fix patches, reasoning). If subagent returns this to main agent, it pollutes main agent's context with useless data since we already processed that CIPE. Wait mode keeps stale data in the subagent, never sending it to main agent.
### Step 4: Progress Tracking
After each action:
- If state changed significantly → reset `no_progress_count = 0`
- If state unchanged → `no_progress_count++`
- On new CI attempt detected → reset `local_verify_count = 0`
## Status Reporting
Based on verbosity level:
| Level | What to Report |
| --------- | -------------------------------------------------------------------------- |
| `minimal` | Only final result (success/failure/timeout) |
| `medium` | State changes + periodic updates ("Cycle N \\| Elapsed: Xm \\| Status: ...") |
| `verbose` | All of medium + full subagent responses, git outputs, MCP responses |
## User Instruction Examples
Users can override default behaviors:
| Instruction | Effect |
| ------------------------------------------------ | --------------------------------------------- |
| "never auto-apply" | Always prompt before applying any fix |
| "always ask before git push" | Prompt before each push |
| "reject any fix for e2e tasks" | Auto-reject if `failedTaskIds` contains e2e |
| "apply all fixes regardless of verification" | Skip verification check, apply everything |
| "if confidence < 70, reject" | Check confidence field before applying |
| "run 'nx affected -t typecheck' before applying" | Add local verification step |
| "auto-fix workflow failures" | Attempt lockfile updates on pre-CIPE failures |
| "wait 45 min for new CIPE" | Override new-CIPE timeout (default: 10 min) |
## Error Handling
| Error | Action |
| ------------------------ | ------------------------------------------------------------------------------------- |
| Git rebase conflict | Report to user, exit |
| `nx apply-locally` fails | Report to user, attempt manual patch or exit |
| MCP tool error | Retry once, if fails report to user |
| Subagent spawn failure | Retry once, if fails exit with error |
| No new CIPE detected | If `--auto-fix-workflow`, try lockfile update; otherwise report to user with guidance |
| Lockfile auto-fix fails | Report to user, exit with guidance to check CI logs |
## Example Session
### Example 1: Normal Flow with Self-Healing (medium verbosity)
```
[ci-monitor] Starting CI monitor for branch 'feature/add-auth'
[ci-monitor] Config: max-cycles=5, timeout=120m, verbosity=medium
[ci-monitor] Spawning subagent to poll CI status...
[CI Monitor] CI attempt: IN_PROGRESS | Self-Healing: NOT_STARTED | Elapsed: 1m
[CI Monitor] CI attempt: FAILED | Self-Healing: IN_PROGRESS | Elapsed: 3m
[CI Monitor] CI attempt: FAILED | Self-Healing: COMPLETED | Elapsed: 5m
[ci-monitor] Fix available! Verification: COMPLETED
[ci-monitor] Applying fix via MCP...
[ci-monitor] Fix applied in CI. Waiting for new CI attempt...
[ci-monitor] Spawning subagent to poll CI status...
[CI Monitor] New CI attempt detected!
[CI Monitor] CI attempt: SUCCEEDED | Elapsed: 8m
[ci-monitor] CI passed successfully!
[ci-monitor] Summary:
- Total cycles: 2
- Total time: 12m 34s
- Fixes applied: 1
- Result: SUCCESS
```
### Example 2: Pre-CI Failure (medium verbosity)
```
[ci-monitor] Starting CI monitor for branch 'feature/add-products'
[ci-monitor] Config: max-cycles=5, timeout=120m, auto-fix-workflow=true
[ci-monitor] Spawning subagent to poll CI status...
[CI Monitor] CI attempt: FAILED | Self-Healing: COMPLETED | Elapsed: 2m
[ci-monitor] Applying fix locally, enhancing, and pushing...
[ci-monitor] Committed: abc1234
[ci-monitor] Spawning subagent to poll CI status...
[CI Monitor] Waiting for new CI attempt... (expected SHA: abc1234)
[CI Monitor] ⚠️ CI attempt timeout (10 min). Returning no_new_cipe.
[ci-monitor] Status: no_new_cipe
[ci-monitor] --auto-fix-workflow enabled. Attempting lockfile update...
[ci-monitor] Lockfile updated. Committed: def5678
[ci-monitor] Spawning subagent to poll CI status...
[CI Monitor] New CI attempt detected!
[CI Monitor] CI attempt: SUCCEEDED | Elapsed: 18m
[ci-monitor] CI passed successfully!
[ci-monitor] Summary:
- Total cycles: 3
- Total time: 22m 15s
- Fixes applied: 1 (self-healing) + 1 (lockfile)
- Result: SUCCESS
```"""
+437
View File
@@ -0,0 +1,437 @@
---
name: ci-monitor
description: Monitor Nx Cloud CI pipeline and handle self-healing fixes automatically. Checks for Nx Cloud connection before starting.
---
# CI Monitor Command
You are the orchestrator for monitoring Nx Cloud CI pipeline executions and handling self-healing fixes. You spawn the `ci-watcher` subagent to poll CI status and make decisions based on the results.
## Context
- **Current Branch:** !`git branch --show-current`
- **Current Commit:** !`git rev-parse --short HEAD`
- **Remote Status:** !`git status -sb | head -1`
## User Instructions
$ARGUMENTS
**Important:** If user provides specific instructions, respect them over default behaviors described below.
## Configuration Defaults
| Setting | Default | Description |
| ------------------------- | ------------- | ------------------------------------------------------------------- |
| `--max-cycles` | 10 | Maximum CIPE cycles before timeout |
| `--timeout` | 120 | Maximum duration in minutes |
| `--verbosity` | medium | Output level: minimal, medium, verbose |
| `--branch` | (auto-detect) | Branch to monitor |
| `--subagent-timeout` | 60 | Subagent polling timeout in minutes |
| `--fresh` | false | Ignore previous context, start fresh |
| `--auto-fix-workflow` | false | Attempt common fixes for pre-CIPE failures (e.g., lockfile updates) |
| `--new-cipe-timeout` | 10 | Minutes to wait for new CIPE after action |
| `--local-verify-attempts` | 3 | Max local verification + enhance cycles before pushing to CI |
Parse any overrides from `$ARGUMENTS` and merge with defaults.
## Nx Cloud Connection Check
**CRITICAL**: Before starting the monitoring loop, verify the workspace is connected to Nx Cloud.
### Step 0: Verify Nx Cloud Connection
1. **Check `nx.json`** at workspace root for `nxCloudId` or `nxCloudAccessToken`
2. **If `nx.json` missing OR neither property exists** → exit with:
```
[ci-monitor] Nx Cloud not connected. Unlock 70% faster CI and auto-fix broken PRs with https://nx.dev/nx-cloud
```
3. **If connected** → continue to main loop
## Session Context Behavior
**Important:** Within a Claude Code session, conversation context persists. If you Ctrl+C to interrupt the monitor and re-run `/ci-monitor`, Claude remembers the previous state and may continue from where it left off.
- **To continue monitoring:** Just re-run `/ci-monitor` (context is preserved)
- **To start fresh:** Use `/ci-monitor --fresh` to ignore previous context
- **For a completely clean slate:** Exit Claude Code and restart `claude`
## Default Behaviors by Status
The subagent returns with one of the following statuses. This table defines the **default behavior** for each status. User instructions can override any of these.
| Status | Default Behavior |
| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `ci_success` | Exit with success. Log "CI passed successfully!" |
| `fix_auto_applying` | Fix will be auto-applied by self-healing. Do NOT call MCP. Record `last_cipe_url`, spawn new subagent in wait mode to poll for new CIPE. |
| `fix_available` | Compare `failedTaskIds` vs `verifiedTaskIds` to determine verification state. See **Fix Available Decision Logic** section below. |
| `fix_failed` | Self-healing failed to generate fix. Attempt local fix based on `taskOutputSummary`. If successful → commit, push, loop. If not → exit with failure. |
| `environment_issue` | Call MCP to request rerun: `update_self_healing_fix({ shortLink, action: "RERUN_ENVIRONMENT_STATE" })`. New CIPE spawns automatically. Loop to poll for new CIPE. |
| `no_fix` | CI failed, no fix available (self-healing disabled or not executable). Attempt local fix if possible. Otherwise exit with failure. |
| `no_new_cipe` | Expected CIPE never spawned (CI workflow likely failed before Nx tasks). Report to user, attempt common fixes if configured, or exit with guidance. |
| `polling_timeout` | Subagent polling timeout reached. Exit with timeout. |
| `cipe_canceled` | CIPE was canceled. Exit with canceled status. |
| `cipe_timed_out` | CIPE timed out. Exit with timeout status. |
| `error` | Increment `no_progress_count`. If >= 3 → exit with circuit breaker. Otherwise wait 60s and loop. |
### Fix Available Decision Logic
When subagent returns `fix_available`, main agent compares `failedTaskIds` vs `verifiedTaskIds`:
#### Step 1: Categorize Tasks
1. **Verified tasks** = tasks in both `failedTaskIds` AND `verifiedTaskIds`
2. **Unverified tasks** = tasks in `failedTaskIds` but NOT in `verifiedTaskIds`
3. **E2E tasks** = unverified tasks where target contains "e2e" (task format: `<project>:<target>` or `<project>:<target>:<config>`)
4. **Verifiable tasks** = unverified tasks that are NOT e2e
#### Step 2: Determine Path
| Condition | Path |
| --------------------------------------- | ---------------------------------------- |
| No unverified tasks (all verified) | Apply via MCP |
| Unverified tasks exist, but ALL are e2e | Apply via MCP (treat as verified enough) |
| Verifiable tasks exist | Local verification flow |
#### Step 3a: Apply via MCP (fully/e2e-only verified)
- Call `update_self_healing_fix({ shortLink, action: "APPLY" })`
- Record `last_cipe_url`, spawn subagent in wait mode
#### Step 3b: Local Verification Flow
When verifiable (non-e2e) unverified tasks exist:
1. **Detect package manager:**
- `pnpm-lock.yaml` exists → `pnpm nx`
- `yarn.lock` exists → `yarn nx`
- Otherwise → `npx nx`
2. **Run verifiable tasks in parallel:**
- Spawn `general` subagents to run each task concurrently
- Each subagent runs: `<pm> nx run <taskId>`
- Collect pass/fail results from all subagents
3. **Evaluate results:**
| Result | Action |
| ------------------------- | ---------------------------- |
| ALL verifiable tasks pass | Apply via MCP |
| ANY verifiable task fails | Apply-locally + enhance flow |
4. **Apply-locally + enhance flow:**
- Run `nx apply-locally <shortLink>`
- Enhance the code to fix failing tasks
- Run failing tasks again to verify fix
- If still failing → increment `local_verify_count`, loop back to enhance
- If passing → commit and push, record `expected_commit_sha`, spawn subagent in wait mode
5. **Track attempts** (wraps step 4):
- Increment `local_verify_count` after each enhance cycle
- If `local_verify_count >= local_verify_attempts` (default: 3):
- Get code in commit-able state
- Commit and push with message indicating local verification failed
- Report to user:
```
[ci-monitor] Local verification failed after <N> attempts. Pushed to CI for final validation. Failed: <taskIds>
```
- Record `expected_commit_sha`, spawn subagent in wait mode (let CI be final judge)
#### Commit Message Format
```bash
git commit -m "fix(<projects>): <brief description>
Failed tasks: <taskId1>, <taskId2>
Local verification: passed|enhanced|failed-pushing-to-ci"
```
### Unverified Fix Flow (No Verification Attempted)
When `verificationStatus` is `FAILED`, `NOT_EXECUTABLE`, or fix has `couldAutoApplyTasks != true` with no verification:
- Analyze fix content (`suggestedFix`, `suggestedFixReasoning`, `taskOutputSummary`)
- If fix looks correct → apply via MCP
- If fix needs enhancement → use Apply Locally + Enhance Flow above
- If fix is wrong → reject via MCP, fix from scratch, commit, push
### Auto-Apply Eligibility
The `couldAutoApplyTasks` field indicates whether the fix is eligible for automatic application:
- **`true`**: Fix is eligible for auto-apply. Subagent keeps polling while verification is in progress. Returns `fix_auto_applying` when verified, or `fix_available` if verification fails.
- **`false`** or **`null`**: Fix requires manual action (apply via MCP, apply locally, or reject)
**Key point**: When subagent returns `fix_auto_applying`, do NOT call MCP to apply - self-healing handles it. Just spawn a new subagent in wait mode.
### Apply vs Reject vs Apply Locally
- **Apply via MCP**: Calls `update_self_healing_fix({ shortLink, action: "APPLY" })`. Self-healing agent applies the fix in CI and a new CIPE spawns automatically. No local git operations needed.
- **Apply Locally**: Runs `nx apply-locally <shortLink>`. Applies the patch to your local working directory and sets state to `APPLIED_LOCALLY`. Use this when you want to enhance the fix before pushing.
- **Reject via MCP**: Calls `update_self_healing_fix({ shortLink, action: "REJECT" })`. Marks fix as rejected. Use only when the fix is completely wrong and you'll fix from scratch.
### Apply Locally + Enhance Flow
When the fix needs enhancement (use `nx apply-locally`, NOT reject):
1. Apply the patch locally: `nx apply-locally <shortLink>` (this also updates state to `APPLIED_LOCALLY`)
2. Make additional changes as needed
3. Commit and push:
```bash
git add -A
git commit -m "fix: resolve <failedTaskIds>"
git push origin $(git branch --show-current)
```
4. Loop to poll for new CIPE
### Reject + Fix From Scratch Flow
When the fix is completely wrong:
1. Call MCP to reject: `update_self_healing_fix({ shortLink, action: "REJECT" })`
2. Fix the issue from scratch locally
3. Commit and push:
```bash
git add -A
git commit -m "fix: resolve <failedTaskIds>"
git push origin $(git branch --show-current)
```
4. Loop to poll for new CIPE
### Environment Issue Handling
When `failureClassification == 'ENVIRONMENT_STATE'`:
1. Call MCP to request rerun: `update_self_healing_fix({ shortLink, action: "RERUN_ENVIRONMENT_STATE" })`
2. New CIPE spawns automatically (no local git operations needed)
3. Loop to poll for new CIPE with `previousCipeUrl` set
### No-New-CIPE Handling
When `status == 'no_new_cipe'`:
This means the expected CIPE was never created - CI likely failed before Nx tasks could run.
1. **Report to user:**
```
[ci-monitor] No CI attempt for <sha> after 10 min. Check CI provider for pre-Nx failures (install, checkout, auth). Last CI attempt: <previousCipeUrl>
```
2. **If user configured auto-fix attempts** (e.g., `--auto-fix-workflow`):
- Detect package manager: check for `pnpm-lock.yaml`, `yarn.lock`, `package-lock.json`
- Run install to update lockfile:
```bash
pnpm install # or npm install / yarn install
```
- If lockfile changed:
```bash
git add pnpm-lock.yaml # or appropriate lockfile
git commit -m "chore: update lockfile"
git push origin $(git branch --show-current)
```
- Record new commit SHA, loop to poll with `expectedCommitSha`
3. **Otherwise:** Exit with `no_new_cipe` status, providing guidance for user to investigate
## Exit Conditions
Exit the monitoring loop when ANY of these conditions are met:
| Condition | Exit Type |
| ------------------------------------------- | ---------------- |
| CI passes (`cipeStatus == 'SUCCEEDED'`) | Success |
| Max CIPE cycles reached | Timeout |
| Max duration reached | Timeout |
| 3 consecutive no-progress iterations | Circuit breaker |
| No fix available and local fix not possible | Failure |
| No new CIPE and auto-fix not configured | Pre-CIPE failure |
| User cancels | Cancelled |
## Main Loop
### Step 1: Initialize Tracking
```
cycle_count = 0
start_time = now()
no_progress_count = 0
local_verify_count = 0
last_state = null
last_cipe_url = null
expected_commit_sha = null
```
### Step 2: Spawn Subagent
Spawn the `ci-watcher` subagent to poll CI status:
**Fresh start (first spawn, no expected CIPE):**
```
Task(
agent: "ci-watcher",
prompt: "Monitor CI for branch '<branch>'.
Subagent timeout: <subagent-timeout> minutes.
New-CIPE timeout: <new-cipe-timeout> minutes.
Verbosity: <verbosity>."
)
```
**After action that triggers new CIPE (wait mode):**
```
Task(
agent: "ci-watcher",
prompt: "Monitor CI for branch '<branch>'.
Subagent timeout: <subagent-timeout> minutes.
New-CIPE timeout: <new-cipe-timeout> minutes.
Verbosity: <verbosity>.
WAIT MODE: A new CIPE should spawn. Ignore old CIPE until new one appears.
Expected commit SHA: <expected_commit_sha>
Previous CIPE URL: <last_cipe_url>"
)
```
### Step 3: Handle Subagent Response
When subagent returns:
1. Check the returned status
2. Look up default behavior in the table above
3. Check if user instructions override the default
4. Execute the appropriate action
5. **If action expects new CIPE**, update tracking (see Step 3a)
6. If action results in looping, go to Step 2
### Step 3a: Track State for New-CIPE Detection
After actions that should trigger a new CIPE, record state before looping:
| Action | What to Track | Subagent Mode |
| ----------------------------- | --------------------------------------------- | ------------- |
| Fix auto-applying | `last_cipe_url = current cipeUrl` | Wait mode |
| Apply via MCP | `last_cipe_url = current cipeUrl` | Wait mode |
| Apply locally + push | `expected_commit_sha = $(git rev-parse HEAD)` | Wait mode |
| Reject + fix + push | `expected_commit_sha = $(git rev-parse HEAD)` | Wait mode |
| Fix failed + local fix + push | `expected_commit_sha = $(git rev-parse HEAD)` | Wait mode |
| No fix + local fix + push | `expected_commit_sha = $(git rev-parse HEAD)` | Wait mode |
| Environment rerun | `last_cipe_url = current cipeUrl` | Wait mode |
| No-new-CIPE + auto-fix + push | `expected_commit_sha = $(git rev-parse HEAD)` | Wait mode |
**CRITICAL**: When passing `expectedCommitSha` or `last_cipe_url` to the subagent, it enters **wait mode**:
- Subagent will **completely ignore** the old/stale CIPE
- Subagent will only wait for new CIPE to appear
- Subagent will NOT return to main agent with stale CIPE data
- Once new CIPE detected, subagent switches to normal polling
**Why wait mode matters for context preservation**: Stale CIPE data can be very large (task output summaries, suggested fix patches, reasoning). If subagent returns this to main agent, it pollutes main agent's context with useless data since we already processed that CIPE. Wait mode keeps stale data in the subagent, never sending it to main agent.
### Step 4: Progress Tracking
After each action:
- If state changed significantly → reset `no_progress_count = 0`
- If state unchanged → `no_progress_count++`
- On new CI attempt detected → reset `local_verify_count = 0`
## Status Reporting
Based on verbosity level:
| Level | What to Report |
| --------- | -------------------------------------------------------------------------- |
| `minimal` | Only final result (success/failure/timeout) |
| `medium` | State changes + periodic updates ("Cycle N \| Elapsed: Xm \| Status: ...") |
| `verbose` | All of medium + full subagent responses, git outputs, MCP responses |
## User Instruction Examples
Users can override default behaviors:
| Instruction | Effect |
| ------------------------------------------------ | --------------------------------------------- |
| "never auto-apply" | Always prompt before applying any fix |
| "always ask before git push" | Prompt before each push |
| "reject any fix for e2e tasks" | Auto-reject if `failedTaskIds` contains e2e |
| "apply all fixes regardless of verification" | Skip verification check, apply everything |
| "if confidence < 70, reject" | Check confidence field before applying |
| "run 'nx affected -t typecheck' before applying" | Add local verification step |
| "auto-fix workflow failures" | Attempt lockfile updates on pre-CIPE failures |
| "wait 45 min for new CIPE" | Override new-CIPE timeout (default: 10 min) |
## Error Handling
| Error | Action |
| ------------------------ | ------------------------------------------------------------------------------------- |
| Git rebase conflict | Report to user, exit |
| `nx apply-locally` fails | Report to user, attempt manual patch or exit |
| MCP tool error | Retry once, if fails report to user |
| Subagent spawn failure | Retry once, if fails exit with error |
| No new CIPE detected | If `--auto-fix-workflow`, try lockfile update; otherwise report to user with guidance |
| Lockfile auto-fix fails | Report to user, exit with guidance to check CI logs |
## Example Session
### Example 1: Normal Flow with Self-Healing (medium verbosity)
```
[ci-monitor] Starting CI monitor for branch 'feature/add-auth'
[ci-monitor] Config: max-cycles=5, timeout=120m, verbosity=medium
[ci-monitor] Spawning subagent to poll CI status...
[CI Monitor] CI attempt: IN_PROGRESS | Self-Healing: NOT_STARTED | Elapsed: 1m
[CI Monitor] CI attempt: FAILED | Self-Healing: IN_PROGRESS | Elapsed: 3m
[CI Monitor] CI attempt: FAILED | Self-Healing: COMPLETED | Elapsed: 5m
[ci-monitor] Fix available! Verification: COMPLETED
[ci-monitor] Applying fix via MCP...
[ci-monitor] Fix applied in CI. Waiting for new CI attempt...
[ci-monitor] Spawning subagent to poll CI status...
[CI Monitor] New CI attempt detected!
[CI Monitor] CI attempt: SUCCEEDED | Elapsed: 8m
[ci-monitor] CI passed successfully!
[ci-monitor] Summary:
- Total cycles: 2
- Total time: 12m 34s
- Fixes applied: 1
- Result: SUCCESS
```
### Example 2: Pre-CI Failure (medium verbosity)
```
[ci-monitor] Starting CI monitor for branch 'feature/add-products'
[ci-monitor] Config: max-cycles=5, timeout=120m, auto-fix-workflow=true
[ci-monitor] Spawning subagent to poll CI status...
[CI Monitor] CI attempt: FAILED | Self-Healing: COMPLETED | Elapsed: 2m
[ci-monitor] Applying fix locally, enhancing, and pushing...
[ci-monitor] Committed: abc1234
[ci-monitor] Spawning subagent to poll CI status...
[CI Monitor] Waiting for new CI attempt... (expected SHA: abc1234)
[CI Monitor] ⚠️ CI attempt timeout (10 min). Returning no_new_cipe.
[ci-monitor] Status: no_new_cipe
[ci-monitor] --auto-fix-workflow enabled. Attempting lockfile update...
[ci-monitor] Lockfile updated. Committed: def5678
[ci-monitor] Spawning subagent to poll CI status...
[CI Monitor] New CI attempt detected!
[CI Monitor] CI attempt: SUCCEEDED | Elapsed: 18m
[ci-monitor] CI passed successfully!
[ci-monitor] Summary:
- Total cycles: 3
- Total time: 22m 15s
- Fixes applied: 1 (self-healing) + 1 (lockfile)
- Result: SUCCESS
```
+228
View File
@@ -0,0 +1,228 @@
---
name: nx-generate
description: Generate code using nx generators. USE WHEN scaffolding code or transforming existing code - for example creating libraries or applications, or anything else that is boilerplate code or automates repetitive tasks. ALWAYS use this first when generating code with Nx instead of calling MCP tools or running nx generate immediately.
---
# Run Nx Generator
Nx generators are powerful tools that scaffold projects, make automated code migrations or automate repetitive tasks in a monorepo. They ensure consistency across the codebase and reduce boilerplate work.
This skill applies when the user wants to:
- Create new projects like libraries or applications
- Scaffold features or boilerplate code
- Run workspace-specific or custom generators
- Do anything else that an nx generator exists for
## Generator Discovery Flow
### Step 1: List Available Generators
Use the Nx CLI to discover available generators:
- List all generators for a plugin: `npx nx list @nx/react`
- View available plugins: `npx nx list`
This includes:
- Plugin generators (e.g., `@nx/react:library`, `@nx/js:library`)
- Local workspace generators (defined in the repo's own plugins)
### Step 2: Match Generator to User Request
Based on the user's request, identify which generator(s) could fulfill their needs. Consider:
- What artifact type they want to create (library, application, etc.)
- Which framework or technology stack is relevant
- Whether they mentioned specific generator names
**IMPORTANT**: When both a local workspace generator and an external plugin generator could satisfy the request, **always prefer the local workspace generator**. Local generators are customized for the specific repo's patterns and conventions.
It's possible that the user request is something that no Nx generator exists for whatsoever. In this case, you can stop using this skill and try to help the user another way. HOWEVER, the burden of proof for this is high. Before aborting, carefully consider each and every generator that's available. Look into details for any that could be related in any way before making this decision.
## Pre-Execution Checklist
Before running any generator, complete these steps:
### 1. Fetch Generator Schema
Use the `--help` flag to understand all available options:
```bash
npx nx g @nx/react:library --help
```
Pay attention to:
- Required options that must be provided
- Optional options that may be relevant to the user's request
- Default values that might need to be overridden
### 2. Read Generator Source Code
Understanding what the generator actually does helps you:
- Know what files will be created/modified
- Understand any side effects (updating configs, installing deps, etc.)
- Identify options that might not be obvious from the schema
To find generator source code:
- For plugin generators: Use `node -e "console.log(require.resolve('@nx/<plugin>/generators.json'));"` to find the generators.json, then locate the source from there
- If that fails, read directly from `node_modules/<plugin>/generators.json`
- For local generators: They are typically in `tools/generators/` or a local plugin directory. You can search the repo for the generator name to find it.
### 2.5 Reevaluate if the generator is right
Once you have built up an understanding of what the selected generator does, reconsider: Is this the right generator to service the user request?
If not, it's okay to go back to the Generator Discovery Flow and select a different generator before proceeding. If you do, make sure to go through the entire pre-execution checklist once more.
### 3. Understand Repo Context
Before generating, examine the target area of the codebase:
- Look at similar existing artifacts (other libraries, applications, etc.)
- Identify patterns and conventions used in the repo
- Note naming conventions, file structures, and configuration patterns
- Try to match these patterns when configuring the generator
For example, if similar libraries are using a specific test runner, build tool or linter, try to match that if possible.
If projects or other artifacts are organized with a specific naming convention, try to match it.
### 4. Validate Required Options
Ensure all required options have values:
- Map the user's request to generator options
- Infer values from context where possible
- Ask the user for any critical missing information
## Execution
Keep in mind that you might have to prefix things with npx/pnpx/yarn if the user doesn't have nx installed globally.
Many generators will behave differently based on where they are executed. For example, first-party nx library generators use the cwd to determine the directory that the library should be placed in. This is highly important.
### Consider Dry-Run (Optional)
Running with `--dry-run` first is strongly encouraged but not mandatory. Use your judgment:
- For complex generators or unfamiliar territory: do a dry-run first
- For simple, well-understood generators: may proceed directly
- Dry-run shows file names and created/deleted/modified markers, but not content
- There are cases where a generator does not support dry-run (for example if it had to install an npm package) - in that case --dry-run might fail. Don't be discouraged but simply move on to running the generator for real and iterating from there.
### Running the Generator
Execute the generator with:
```bash
nx generate <generator-name> <options> --no-interactive
```
**CRITICAL**: Always include `--no-interactive` to prevent prompts that would hang the execution.
Example:
```bash
nx generate @nx/react:library --name=my-utils --no-interactive
```
### Handling Generator Failures
If the generator fails:
1. **Diagnose the error** - Read the error message carefully
2. **Identify the cause** - Missing options, invalid values, conflicts, etc.
3. **Attempt automatic fix** - Adjust options or resolve conflicts
4. **Retry** - Run the generator again with corrected options
Common failure reasons:
- Missing required options
- Invalid option values
- Conflicting with existing files
- Missing dependencies
- Generator doesn't support certain flag combinations
## Post-Generation
### 1. Modify Generated Code (If Needed)
Generators provide a starting point, but the output may need adjustment to match the user's specific requirements:
- Add or modify functionality as requested
- Adjust imports, exports, or configurations
- Integrate with existing code patterns in the repo
### 2. Format Code
Run formatting on all generated/modified files:
```bash
nx format --fix
```
Languages other than javascript/typescript might need other formatting invocations too.
### 3. Run Verification
Verify that the generated code works correctly. What this looks like will vary depending on the type of generator and the targets available.
If the generator created a new project, run its targets directly
Use your best judgement to determine what needs to be verified.
Example:
```bash
nx lint <new-project>
nx test <new-project>
nx build <new-project>
```
### 4. Handle Verification Failures
When verification fails:
**If scope is manageable** (a few lint errors, minor type issues):
- Fix the issues
- Re-run verification to confirm
**If issues are extensive** (many errors, complex problems):
- Attempt simple, obvious fixes first
- If still failing, escalate to the user with:
- Description of what was generated
- What verification is failing
- What you've attempted to fix
- Remaining issues that need user input
## Error Handling
### Generator Failures
- Check the error message for specific causes
- Verify all required options are provided
- Check for conflicts with existing files
- Ensure the generator name and options are correct
### Missing Options
- Consult the generator schema for required fields
- Infer values from context when reasonable
- Ask the user for values that cannot be inferred
## Key Principles
1. **Local generators first** - Always prefer workspace/local generators over external plugin generators when both could work
2. **Understand before running** - Read both the schema AND the source code to fully understand what will happen
3. **No prompts** - Always use `--no-interactive` to prevent hanging
4. **Generators are starting points** - Modify the output as needed to fully satisfy the user's requirements
5. **Verify changes work** - Don't just generate; ensure the code builds, lints, and tests pass
6. **Be proactive about fixes** - Don't just report errors; attempt to resolve them automatically when possible
7. **Match repo patterns** - Study existing similar code in the repo and match its conventions
+9
View File
@@ -0,0 +1,9 @@
---
name: nx-plugins
description: Find and add Nx plugins. USE WHEN user wants to discover available plugins, install a new plugin, or add support for a specific framework or technology to the workspace.
---
## Finding and Installing new plugins
- List plugins: `pnpm nx list`
- Install plugins `pnpm nx add <plugin>`. Example: `pnpm nx add @nx/react`.
+58
View File
@@ -0,0 +1,58 @@
---
name: nx-run-tasks
description: Helps with running tasks in an Nx workspace. USE WHEN the user wants to execute build, test, lint, serve, or run any other tasks defined in the workspace.
---
You can run tasks with Nx in the following way.
Keep in mind that you might have to prefix things with npx/pnpx/yarn if the user doesn't have nx installed globally. Look at the package.json or lockfile to determine which package manager is in use.
For more details on any command, run it with `--help` (e.g. `nx run-many --help`, `nx affected --help`).
## Understand which tasks can be run
You can check those via `nx show project <projectname> --json`, for example `nx show project myapp --json`. It contains a `targets` section which has information about targets that can be run. You can also just look at the `package.json` scripts or `project.json` targets, but you might miss out on inferred tasks by Nx plugins.
## Run a single task
```
nx run <project>:<task>
```
where `project` is the project name defined in `package.json` or `project.json` (if present).
## Run multiple tasks
```
nx run-many -t build test lint typecheck
```
You can pass a `-p` flag to filter to specific projects, otherwise it runs on all projects. You can also use `--exclude` to exclude projects, and `--parallel` to control the number of parallel processes (default is 3).
Examples:
- `nx run-many -t test -p proj1 proj2` — test specific projects
- `nx run-many -t test --projects=*-app --exclude=excluded-app` — test projects matching a pattern
- `nx run-many -t test --projects=tag:api-*` — test projects by tag
## Run tasks for affected projects
Use `nx affected` to only run tasks on projects that have been changed and projects that depend on changed projects. This is especially useful in CI and for large workspaces.
```
nx affected -t build test lint
```
By default it compares against the base branch. You can customize this:
- `nx affected -t test --base=main --head=HEAD` — compare against a specific base and head
- `nx affected -t test --files=libs/mylib/src/index.ts` — specify changed files directly
## Useful flags
These flags work with `run`, `run-many`, and `affected`:
- `--skipNxCache` — rerun tasks even when results are cached
- `--verbose` — print additional information such as stack traces
- `--nxBail` — stop execution after the first failed task
- `--configuration=<name>` — use a specific configuration (e.g. `production`)
+186
View File
@@ -0,0 +1,186 @@
---
name: nx-workspace
description: "Explore and understand Nx workspaces. USE WHEN answering any questions about the nx workspace, the projects in it or tasks to run. EXAMPLES: 'What projects are in this workspace?', 'How is project X configured?', 'What targets can I run?', 'What's affected by my changes?', 'Which projects depend on library Y?', or any questions about Nx workspace structure, project configuration, or available tasks."
---
# Nx Workspace Exploration
This skill provides read-only exploration of Nx workspaces. Use it to understand workspace structure, project configuration, available targets, and dependencies.
Keep in mind that you might have to prefix commands with `npx`/`pnpx`/`yarn` if nx isn't installed globally. Check the lockfile to determine the package manager in use.
## Listing Projects
Use `nx show projects` to list projects in the workspace.
```bash
# List all projects
nx show projects
# Filter by pattern (glob)
nx show projects --projects "apps/*"
nx show projects --projects "shared-*"
# Filter by project type
nx show projects --type app
nx show projects --type lib
nx show projects --type e2e
# Filter by target (projects that have a specific target)
nx show projects --withTarget build
nx show projects --withTarget e2e
# Find affected projects (changed since base branch)
nx show projects --affected
nx show projects --affected --base=main
nx show projects --affected --type app
# Combine filters
nx show projects --type lib --withTarget test
nx show projects --affected --exclude="*-e2e"
# Output as JSON
nx show projects --json
```
## Project Configuration
Use `nx show project <name> --json` to get the full resolved configuration for a project.
**Important**: Do NOT read `project.json` directly - it only contains partial configuration. The `nx show project` command returns the full resolved config including inferred targets from plugins.
You can read the full project schema at `node_modules/nx/schemas/project-schema.json` to understand nx project configuration options.
```bash
# Get full project configuration
nx show project my-app --json
# Extract specific parts from the JSON
nx show project my-app --json | jq '.targets'
nx show project my-app --json | jq '.targets.build'
nx show project my-app --json | jq '.targets | keys'
# Check project metadata
nx show project my-app --json | jq '{name, root, sourceRoot, projectType, tags}'
```
## Target Information
Targets define what tasks can be run on a project.
```bash
# List all targets for a project
nx show project my-app --json | jq '.targets | keys'
# Get full target configuration
nx show project my-app --json | jq '.targets.build'
# Check target executor/command
nx show project my-app --json | jq '.targets.build.executor'
nx show project my-app --json | jq '.targets.build.command'
# View target options
nx show project my-app --json | jq '.targets.build.options'
# Check target inputs/outputs (for caching)
nx show project my-app --json | jq '.targets.build.inputs'
nx show project my-app --json | jq '.targets.build.outputs'
# Find projects with a specific target
nx show projects --withTarget serve
nx show projects --withTarget e2e
```
## Workspace Configuration
Read `nx.json` directly for workspace-level configuration.
You can read the full project schema at `node_modules/nx/schemas/nx-schema.json` to understand nx project configuration options.
```bash
# Read the full nx.json
cat nx.json
# Or use jq for specific sections
cat nx.json | jq '.targetDefaults'
cat nx.json | jq '.namedInputs'
cat nx.json | jq '.plugins'
cat nx.json | jq '.generators'
```
Key nx.json sections:
- `targetDefaults` - Default configuration applied to all targets of a given name
- `namedInputs` - Reusable input definitions for caching
- `plugins` - Nx plugins and their configuration
- ...and much more, read the schema or nx.json for details
## Affected Projects
Find projects affected by changes in the current branch.
```bash
# Affected since base branch (auto-detected)
nx show projects --affected
# Affected with explicit base
nx show projects --affected --base=main
nx show projects --affected --base=origin/main
# Affected between two commits
nx show projects --affected --base=abc123 --head=def456
# Affected apps only
nx show projects --affected --type app
# Affected excluding e2e projects
nx show projects --affected --exclude="*-e2e"
# Affected by uncommitted changes
nx show projects --affected --uncommitted
# Affected by untracked files
nx show projects --affected --untracked
```
## Common Exploration Patterns
### "What's in this workspace?"
```bash
nx show projects
nx show projects --type app
nx show projects --type lib
```
### "How do I build/test/lint project X?"
```bash
nx show project X --json | jq '.targets | keys'
nx show project X --json | jq '.targets.build'
```
### "What depends on library Y?"
```bash
# Find projects that may depend on Y by searching for imports
# (Nx doesn't have a direct "dependents" command via CLI)
grep -r "from '@myorg/Y'" --include="*.ts" --include="*.tsx" apps/ libs/
```
### "What configuration options are available?"
```bash
cat node_modules/nx/schemas/nx-schema.json | jq '.properties | keys'
cat node_modules/nx/schemas/project-schema.json | jq '.properties | keys'
```
### "Why is project X affected?"
```bash
# Check what files changed
git diff --name-only main
# See which project owns those files
nx show project X --json | jq '.root'
```
+478
View File
@@ -0,0 +1,478 @@
---
description: Polls Nx Cloud CI pipeline and self-healing status. Returns structured state when actionable. Spawned by /nx-cloud-ci-monitor command to monitor CI Attempt status.
---
# CI Watcher Subagent
You are a CI monitoring subagent responsible for polling Nx Cloud CI Attempt status and self-healing state. You report status back to the main agent - you do NOT make apply/reject decisions.
## Your Responsibilities
1. Poll CI status using the `ci_information` MCP tool
2. Implement exponential backoff between polls
3. Return structured state when an actionable condition is reached
4. Track iteration count and elapsed time
5. Output status updates based on verbosity level
## Input Parameters (from Main Agent)
The main agent may provide these optional parameters in the prompt:
| Parameter | Description |
| ------------------- | -------------------------------------------------------- |
| `branch` | Branch to monitor (auto-detected if not provided) |
| `expectedCommitSha` | Commit SHA that should trigger a new CI Attempt |
| `previousCipeUrl` | CI Attempt URL before the action (to detect change) |
| `subagentTimeout` | Polling timeout in minutes (default: 60) |
| `verbosity` | Output level: minimal, medium, verbose (default: medium) |
When `expectedCommitSha` or `previousCipeUrl` is provided, you must detect whether a new CI Attempt has spawned.
## MCP Tool Reference
### `ci_information`
**Input:**
```json
{
"branch": "string (optional, defaults to current git branch)",
"select": "string (optional, comma-separated field names)",
"pageToken": "number (optional, 0-based pagination for long strings)"
}
```
**Output:**
```json
{
"cipeStatus": "NOT_STARTED | IN_PROGRESS | SUCCEEDED | FAILED | CANCELED | TIMED_OUT",
"cipeUrl": "string",
"branch": "string",
"commitSha": "string | null",
"failedTaskIds": "string[]",
"verifiedTaskIds": "string[]",
"selfHealingEnabled": "boolean",
"selfHealingStatus": "NOT_STARTED | IN_PROGRESS | COMPLETED | FAILED | NOT_EXECUTABLE | null",
"verificationStatus": "NOT_STARTED | IN_PROGRESS | COMPLETED | FAILED | NOT_EXECUTABLE | null",
"userAction": "NONE | APPLIED | REJECTED | APPLIED_LOCALLY | APPLIED_AUTOMATICALLY | null",
"failureClassification": "string | null",
"taskOutputSummary": "string | null",
"suggestedFixReasoning": "string | null",
"suggestedFixDescription": "string | null",
"suggestedFix": "string | null",
"shortLink": "string | null",
"couldAutoApplyTasks": "boolean | null",
"confidence": "number | null",
"confidenceReasoning": "string | null"
}
```
**Select Parameter:**
| Usage | Returns |
| --------------- | ----------------------------------------------------------- |
| No `select` | Formatted overview (truncated, not recommended for polling) |
| Single field | Raw value with pagination for long strings |
| Multiple fields | Object with requested field values |
**Field Sets for Efficient Polling:**
```yaml
WAIT_FIELDS:
'cipeUrl,commitSha,cipeStatus'
# Minimal fields for detecting new CI Attempt
LIGHT_FIELDS:
'cipeStatus,cipeUrl,branch,commitSha,selfHealingStatus,verificationStatus,userAction,failedTaskIds,verifiedTaskIds,selfHealingEnabled,failureClassification,couldAutoApplyTasks,shortLink,confidence,confidenceReasoning'
# Status fields for determining actionable state
HEAVY_FIELDS:
'taskOutputSummary,suggestedFix,suggestedFixReasoning,suggestedFixDescription'
# Large content fields - fetch only when returning to main agent
```
## Initial Wait
Before first poll, wait based on context:
- **Fresh start (no expected CIPE):** Wait 60 seconds to allow CI to start
- **Expecting new CIPE:** Wait 30 seconds (action already triggered)
**IMPORTANT:** Always run sleep in foreground, NOT as background command.
```bash
sleep 60 # or 30 if expecting new CIPE (FOREGROUND, not background)
```
## Two-Phase Operation
The subagent operates in one of two modes depending on input:
### Mode 1: Fresh Start (no `expectedCommitSha` or `previousCipeUrl`)
Normal polling - process whatever CIPE is returned by `ci_information`.
### Mode 2: Wait-for-New-CIPE (when `expectedCommitSha` or `previousCipeUrl` provided)
**CRITICAL**: When expecting a new CIPE, the subagent must **completely ignore** the old/stale CIPE. Do NOT process its status, do NOT return actionable states based on it.
#### Phase A: Wait Mode
1. Start a **new-CIPE timeout** timer (default: 30 minutes)
2. On each poll of `ci_information`:
- Check if CIPE is NEW:
- `cipeUrl` differs from `previousCipeUrl`**new CIPE detected**
- `commitSha` matches `expectedCommitSha`**correct CIPE detected**
- If still OLD CIPE: **ignore all status fields**, just wait and poll again
- Do NOT return `fix_available`, `ci_success`, etc. based on old CIPE!
3. Output wait status (see below)
4. If timeout (30 min) reached → return `no_new_cipe`
#### Phase B: Normal Polling (after new CIPE detected)
Once new CIPE is detected:
1. Clear the new-CIPE timeout
2. Switch to normal polling mode
3. Process the NEW CIPE's status normally
4. Return when actionable state reached
### Wait Mode Output
While in wait mode, output clearly that you're waiting (not processing):
```
[CI Monitor] ═══════════════════════════════════════════════════════
[CI Monitor] WAIT MODE - Expecting new CI Attempt
[CI Monitor] Expected SHA: <expectedCommitSha>
[CI Monitor] Previous CI Attempt: <previousCipeUrl>
[CI Monitor] ═══════════════════════════════════════════════════════
[CI Monitor] Polling... (elapsed: 0m 30s)
[CI Monitor] Still seeing previous CI Attempt (ignoring): <oldCipeUrl>
[CI Monitor] Polling... (elapsed: 1m 30s)
[CI Monitor] Still seeing previous CI Attempt (ignoring): <oldCipeUrl>
[CI Monitor] Polling... (elapsed: 2m 30s)
[CI Monitor] ✓ New CI Attempt detected! URL: <newCipeUrl>, SHA: <newCommitSha>
[CI Monitor] Switching to normal polling mode...
```
### Why This Matters (Context Preservation)
**The problem**: Stale CIPE data can be very large:
- `taskOutputSummary`: potentially thousands of characters of build/test output
- `suggestedFix`: entire patch files
- `suggestedFixReasoning`: detailed explanation
If subagent returns stale CIPE data to main agent, it **pollutes main agent's context** with useless information (we already processed that CIPE). This wastes valuable context window.
**Without wait mode:**
1. Poll `ci_information` → get old CIPE with huge data
2. Return to main agent with all that stale data
3. Main agent's context gets polluted with useless info
4. Main agent has to process/ignore it anyway
**With wait mode:**
1. Poll `ci_information` → get old CIPE → **ignore it, don't return**
2. Keep waiting internally (stale data stays in subagent)
3. New CIPE appears → switch to normal mode
4. Return to main agent with only the NEW, relevant CIPE data
## Polling Loop
### Subagent State Management
Maintain internal accumulated state across polls:
```
accumulated_state = {}
```
### Call `ci_information` MCP Tool
**Wait Mode (expecting new CI Attempt):**
```
ci_information({
branch: "<branch_name>",
select: "cipeUrl,commitSha,cipeStatus"
})
```
Only fetch minimal fields needed to detect CI Attempt change. Do NOT fetch heavy fields - stale data wastes context.
**Normal Mode (processing CI Attempt):**
```
ci_information({
branch: "<branch_name>",
select: "cipeStatus,cipeUrl,branch,commitSha,selfHealingStatus,verificationStatus,userAction,failedTaskIds,verifiedTaskIds,selfHealingEnabled,failureClassification,couldAutoApplyTasks,shortLink,confidence,confidenceReasoning"
})
```
Merge response into `accumulated_state` after each poll.
### Analyze Response
**If in Wait Mode** (expecting new CIPE):
1. Check if CIPE is new (see Two-Phase Operation above)
2. If old CIPE → **ignore status**, output wait message, poll again
3. If new CIPE → switch to normal mode, continue below
**If in Normal Mode**:
Based on the response, decide whether to **keep polling** or **return to main agent**.
### Keep Polling When
Continue polling (with backoff) if ANY of these conditions are true:
| Condition | Reason |
| --------------------------------------- | ---------------------------------------- |
| `cipeStatus == 'IN_PROGRESS'` | CI still running |
| `cipeStatus == 'NOT_STARTED'` | CI hasn't started yet |
| `selfHealingStatus == 'IN_PROGRESS'` | Self-healing agent working |
| `selfHealingStatus == 'NOT_STARTED'` | Self-healing not started yet |
| `failureClassification == 'FLAKY_TASK'` | Auto-rerun in progress |
| `userAction == 'APPLIED_AUTOMATICALLY'` | New CI Attempt spawning after auto-apply |
When `couldAutoApplyTasks == true`:
- `verificationStatus` = `NOT_STARTED`, `IN_PROGRESS` → keep polling (verification still in progress)
- `verificationStatus` = `COMPLETED` → return `fix_auto_applying` (auto-apply will happen, main agent spawns wait mode subagent)
- `verificationStatus` = `FAILED`, `NOT_EXECUTABLE` → return `fix_available` (auto-apply won't happen, needs manual action)
### Exponential Backoff
Between polls, wait with exponential backoff:
| Poll Attempt | Wait Time |
| ------------ | ----------------- |
| 1st | 60 seconds |
| 2nd | 90 seconds |
| 3rd+ | 120 seconds (cap) |
Reset to 60 seconds when state changes significantly.
**IMPORTANT:** Run sleep in foreground (NOT as background command). Background sleep causes "What should Claude do?" prompts when completed.
```bash
# Example backoff - run in FOREGROUND
sleep 60 # First wait
sleep 90 # Second wait
sleep 120 # Third and subsequent waits (capped)
```
### Fetch Heavy Fields on Actionable State
Before returning to main agent, fetch heavy fields if the status requires them:
| Status | Heavy Fields Needed |
| ------------------- | ------------------------------------------------------------------------------ |
| `ci_success` | None |
| `fix_auto_applying` | None |
| `fix_available` | `taskOutputSummary,suggestedFix,suggestedFixReasoning,suggestedFixDescription` |
| `fix_failed` | `taskOutputSummary` |
| `no_fix` | `taskOutputSummary` |
| `environment_issue` | None |
| `no_new_cipe` | None |
| `polling_timeout` | None |
| `cipe_canceled` | None |
| `cipe_timed_out` | None |
```
# Example: fetching heavy fields for fix_available
ci_information({
branch: "<branch_name>",
select: "taskOutputSummary,suggestedFix,suggestedFixReasoning,suggestedFixDescription"
})
```
Merge response into `accumulated_state`, then return merged state to main agent.
**Pagination:** Heavy string fields return first page only. If `hasMore` indicated, include in return format so main agent knows more content available.
### Return to Main Agent When
Return immediately with structured state if ANY of these conditions are true:
| Status | Condition |
| ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `ci_success` | `cipeStatus == 'SUCCEEDED'` |
| `fix_auto_applying` | `selfHealingStatus == 'COMPLETED'` AND `couldAutoApplyTasks == true` AND `verificationStatus == 'COMPLETED'` |
| `fix_available` | `selfHealingStatus == 'COMPLETED'` AND `suggestedFix != null` AND (`couldAutoApplyTasks != true` OR `verificationStatus` in (`FAILED`, `NOT_EXECUTABLE`)) |
| `fix_failed` | `selfHealingStatus == 'FAILED'` |
| `environment_issue` | `failureClassification == 'ENVIRONMENT_STATE'` |
| `no_fix` | `cipeStatus == 'FAILED'` AND (`selfHealingEnabled == false` OR `selfHealingStatus == 'NOT_EXECUTABLE'`) |
| `no_new_cipe` | `expectedCommitSha` or `previousCipeUrl` provided, but no new CI Attempt detected after 30 min |
| `polling_timeout` | Subagent has been polling for > configured timeout (default 60 min) |
| `cipe_canceled` | `cipeStatus == 'CANCELED'` |
| `cipe_timed_out` | `cipeStatus == 'TIMED_OUT'` |
## Subagent Timeout
Track elapsed time. If you have been polling for more than **60 minutes** (configurable via main agent), return with `status: polling_timeout`.
## Return Format
When returning to the main agent, provide a structured response with accumulated state:
```
## CI Monitor Result
**Status:** <status>
**Iterations:** <count>
**Elapsed:** <minutes>m <seconds>s
### CI Attempt Details
- **Status:** <cipeStatus>
- **URL:** <cipeUrl>
- **Branch:** <branch>
- **Commit:** <commitSha>
- **Failed Tasks:** <failedTaskIds>
- **Verified Tasks:** <verifiedTaskIds>
### Self-Healing Details
- **Enabled:** <selfHealingEnabled>
- **Status:** <selfHealingStatus>
- **Verification:** <verificationStatus>
- **User Action:** <userAction>
- **Classification:** <failureClassification>
- **Confidence:** <confidence>
- **Confidence Reasoning:** <confidenceReasoning>
### Fix Information (if available)
- **Short Link:** <shortLink>
- **Description:** <suggestedFixDescription>
- **Reasoning:** <suggestedFixReasoning>
### Task Output Summary (first page)
<taskOutputSummary>
[MORE_CONTENT_AVAILABLE: taskOutputSummary, pageToken: 1]
### Suggested Fix (first page)
<suggestedFix>
[MORE_CONTENT_AVAILABLE: suggestedFix, pageToken: 1]
```
### Pagination Indicators
When a heavy field has more content available, append indicator:
```
[MORE_CONTENT_AVAILABLE: <fieldName>, pageToken: <nextPage>]
```
Main agent can fetch additional pages if needed using:
```
ci_information({ select: "<fieldName>", pageToken: <nextPage> })
```
Fields that may have pagination:
- `taskOutputSummary` (reverse pagination - page 0 = most recent)
- `suggestedFix` (forward pagination - page 0 = start)
- `suggestedFixReasoning`
### Return Format for `no_new_cipe`
When returning with `status: no_new_cipe`, include additional context:
```
## CI Monitor Result
**Status:** no_new_cipe
**Iterations:** <count>
**Elapsed:** <minutes>m <seconds>s
### Expected CI Attempt Not Found
- **Expected Commit SHA:** <expectedCommitSha>
- **Previous CI Attempt URL:** <previousCipeUrl>
- **Last Seen CI Attempt URL:** <cipeUrl>
- **Last Seen Commit SHA:** <commitSha>
- **New CI Attempt Timeout:** 30 minutes (exceeded)
### Likely Cause
CI workflow failed before Nx tasks could run (e.g., install step, checkout, auth).
Check your CI provider logs for the commit <expectedCommitSha>.
### Last Known CI Attempt State
- **Status:** <cipeStatus>
- **Branch:** <branch>
```
## Status Reporting (Verbosity-Controlled)
Output is controlled by the `verbosity` parameter from the main agent:
| Level | What to Output |
| --------- | ----------------------------------------------------------------- |
| `minimal` | No intermediate output. Only return final result when actionable. |
| `medium` | Output only on significant state changes (not every poll). |
| `verbose` | Output detailed phase information after every poll. |
### Minimal Verbosity
No output during polling. Poll silently and return when done.
### Medium Verbosity (Default)
Output **only when state changes significantly** to save context tokens:
- `cipeStatus` changes (e.g., IN_PROGRESS → FAILED)
- `selfHealingStatus` changes (e.g., IN_PROGRESS → COMPLETED)
- New CI Attempt detected (in wait mode)
Format: single line, no decorators:
```
[CI Monitor] CI: FAILED | Self-Healing: IN_PROGRESS | Elapsed: 4m
```
### Verbose Verbosity
Output detailed phase box after every poll:
```
[CI Monitor] ─────────────────────────────────────────────────────
[CI Monitor] Iteration <N> | Elapsed: <X>m <Y>s
[CI Monitor]
[CI Monitor] CI Status: <cipeStatus>
[CI Monitor] Self-Healing: <selfHealingStatus>
[CI Monitor] Verification: <verificationStatus>
[CI Monitor] Classification: <failureClassification>
[CI Monitor]
[CI Monitor] → <human-readable phase description>
[CI Monitor] ─────────────────────────────────────────────────────
```
### Phase Descriptions (for verbose output)
| Status Combo | Description |
| ----------------------------------------------------------------------------------------- | ------------------------------------------- |
| `cipeStatus: IN_PROGRESS` | "CI running..." |
| `cipeStatus: NOT_STARTED` | "Waiting for CI to start..." |
| `cipeStatus: FAILED` + `selfHealingStatus: NOT_STARTED` | "CI failed. Self-healing starting..." |
| `cipeStatus: FAILED` + `selfHealingStatus: IN_PROGRESS` | "CI failed. Self-healing generating fix..." |
| `cipeStatus: FAILED` + `selfHealingStatus: COMPLETED` + `verificationStatus: IN_PROGRESS` | "Fix generated! Verification running..." |
| `cipeStatus: FAILED` + `selfHealingStatus: COMPLETED` + `verificationStatus: COMPLETED` | "Fix ready! Verified successfully." |
| `cipeStatus: FAILED` + `selfHealingStatus: COMPLETED` + `verificationStatus: FAILED` | "Fix generated but verification failed." |
| `cipeStatus: FAILED` + `selfHealingStatus: FAILED` | "Self-healing could not generate a fix." |
| `cipeStatus: SUCCEEDED` | "CI passed!" |
## Important Notes
- You do NOT make apply/reject decisions - that's the main agent's job
- You do NOT perform git operations
- You only poll and report state
- Respect the `verbosity` parameter for output (default: medium)
- If `ci_information` returns an error, wait and retry (count as failed poll)
- Track consecutive failures - if 5 consecutive failures, return with `status: error`
- When expecting new CI Attempt, track the 30-minute new-CI-Attempt timeout separately from the main polling timeout
+18
View File
@@ -0,0 +1,18 @@
# This configuration is here to prevent false positive alerts for __fixtures__.
# We are intentionally disabling the PR opening feature.
version: 2
updates:
- package-ecosystem: 'npm'
directory: '/'
schedule:
interval: 'weekly'
open-pull-requests-limit: 0
exclude-paths:
- '**/__fixtures__/**'
- package-ecosystem: 'github-actions'
directory: '/'
schedule:
interval: 'weekly'
open-pull-requests-limit: 0
+437
View File
@@ -0,0 +1,437 @@
---
description: Monitor Nx Cloud CI pipeline and handle self-healing fixes automatically. Checks for Nx Cloud connection before starting.
argument-hint: '[instructions] [--max-cycles N] [--timeout MINUTES] [--verbosity minimal|medium|verbose] [--branch BRANCH] [--fresh] [--auto-fix-workflow] [--new-cipe-timeout MINUTES]'
---
# CI Monitor Command
You are the orchestrator for monitoring Nx Cloud CI pipeline executions and handling self-healing fixes. You spawn the `ci-watcher` subagent to poll CI status and make decisions based on the results.
## Context
- **Current Branch:** !`git branch --show-current`
- **Current Commit:** !`git rev-parse --short HEAD`
- **Remote Status:** !`git status -sb | head -1`
## User Instructions
${input:args}
**Important:** If user provides specific instructions, respect them over default behaviors described below.
## Configuration Defaults
| Setting | Default | Description |
| ------------------------- | ------------- | ------------------------------------------------------------------- |
| `--max-cycles` | 10 | Maximum CIPE cycles before timeout |
| `--timeout` | 120 | Maximum duration in minutes |
| `--verbosity` | medium | Output level: minimal, medium, verbose |
| `--branch` | (auto-detect) | Branch to monitor |
| `--subagent-timeout` | 60 | Subagent polling timeout in minutes |
| `--fresh` | false | Ignore previous context, start fresh |
| `--auto-fix-workflow` | false | Attempt common fixes for pre-CIPE failures (e.g., lockfile updates) |
| `--new-cipe-timeout` | 10 | Minutes to wait for new CIPE after action |
| `--local-verify-attempts` | 3 | Max local verification + enhance cycles before pushing to CI |
Parse any overrides from `${input:args}` and merge with defaults.
## Nx Cloud Connection Check
**CRITICAL**: Before starting the monitoring loop, verify the workspace is connected to Nx Cloud.
### Step 0: Verify Nx Cloud Connection
1. **Check `nx.json`** at workspace root for `nxCloudId` or `nxCloudAccessToken`
2. **If `nx.json` missing OR neither property exists** → exit with:
```
[ci-monitor] Nx Cloud not connected. Unlock 70% faster CI and auto-fix broken PRs with https://nx.dev/nx-cloud
```
3. **If connected** → continue to main loop
## Session Context Behavior
**Important:** Within a Claude Code session, conversation context persists. If you Ctrl+C to interrupt the monitor and re-run `/ci-monitor`, Claude remembers the previous state and may continue from where it left off.
- **To continue monitoring:** Just re-run `/ci-monitor` (context is preserved)
- **To start fresh:** Use `/ci-monitor --fresh` to ignore previous context
- **For a completely clean slate:** Exit Claude Code and restart `claude`
## Default Behaviors by Status
The subagent returns with one of the following statuses. This table defines the **default behavior** for each status. User instructions can override any of these.
| Status | Default Behavior |
| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `ci_success` | Exit with success. Log "CI passed successfully!" |
| `fix_auto_applying` | Fix will be auto-applied by self-healing. Do NOT call MCP. Record `last_cipe_url`, spawn new subagent in wait mode to poll for new CIPE. |
| `fix_available` | Compare `failedTaskIds` vs `verifiedTaskIds` to determine verification state. See **Fix Available Decision Logic** section below. |
| `fix_failed` | Self-healing failed to generate fix. Attempt local fix based on `taskOutputSummary`. If successful → commit, push, loop. If not → exit with failure. |
| `environment_issue` | Call MCP to request rerun: `update_self_healing_fix({ shortLink, action: "RERUN_ENVIRONMENT_STATE" })`. New CIPE spawns automatically. Loop to poll for new CIPE. |
| `no_fix` | CI failed, no fix available (self-healing disabled or not executable). Attempt local fix if possible. Otherwise exit with failure. |
| `no_new_cipe` | Expected CIPE never spawned (CI workflow likely failed before Nx tasks). Report to user, attempt common fixes if configured, or exit with guidance. |
| `polling_timeout` | Subagent polling timeout reached. Exit with timeout. |
| `cipe_canceled` | CIPE was canceled. Exit with canceled status. |
| `cipe_timed_out` | CIPE timed out. Exit with timeout status. |
| `error` | Increment `no_progress_count`. If >= 3 → exit with circuit breaker. Otherwise wait 60s and loop. |
### Fix Available Decision Logic
When subagent returns `fix_available`, main agent compares `failedTaskIds` vs `verifiedTaskIds`:
#### Step 1: Categorize Tasks
1. **Verified tasks** = tasks in both `failedTaskIds` AND `verifiedTaskIds`
2. **Unverified tasks** = tasks in `failedTaskIds` but NOT in `verifiedTaskIds`
3. **E2E tasks** = unverified tasks where target contains "e2e" (task format: `<project>:<target>` or `<project>:<target>:<config>`)
4. **Verifiable tasks** = unverified tasks that are NOT e2e
#### Step 2: Determine Path
| Condition | Path |
| --------------------------------------- | ---------------------------------------- |
| No unverified tasks (all verified) | Apply via MCP |
| Unverified tasks exist, but ALL are e2e | Apply via MCP (treat as verified enough) |
| Verifiable tasks exist | Local verification flow |
#### Step 3a: Apply via MCP (fully/e2e-only verified)
- Call `update_self_healing_fix({ shortLink, action: "APPLY" })`
- Record `last_cipe_url`, spawn subagent in wait mode
#### Step 3b: Local Verification Flow
When verifiable (non-e2e) unverified tasks exist:
1. **Detect package manager:**
- `pnpm-lock.yaml` exists → `pnpm nx`
- `yarn.lock` exists → `yarn nx`
- Otherwise → `npx nx`
2. **Run verifiable tasks in parallel:**
- Spawn `general` subagents to run each task concurrently
- Each subagent runs: `<pm> nx run <taskId>`
- Collect pass/fail results from all subagents
3. **Evaluate results:**
| Result | Action |
| ------------------------- | ---------------------------- |
| ALL verifiable tasks pass | Apply via MCP |
| ANY verifiable task fails | Apply-locally + enhance flow |
4. **Apply-locally + enhance flow:**
- Run `nx apply-locally <shortLink>`
- Enhance the code to fix failing tasks
- Run failing tasks again to verify fix
- If still failing → increment `local_verify_count`, loop back to enhance
- If passing → commit and push, record `expected_commit_sha`, spawn subagent in wait mode
5. **Track attempts** (wraps step 4):
- Increment `local_verify_count` after each enhance cycle
- If `local_verify_count >= local_verify_attempts` (default: 3):
- Get code in commit-able state
- Commit and push with message indicating local verification failed
- Report to user:
```
[ci-monitor] Local verification failed after <N> attempts. Pushed to CI for final validation. Failed: <taskIds>
```
- Record `expected_commit_sha`, spawn subagent in wait mode (let CI be final judge)
#### Commit Message Format
```bash
git commit -m "fix(<projects>): <brief description>
Failed tasks: <taskId1>, <taskId2>
Local verification: passed|enhanced|failed-pushing-to-ci"
```
### Unverified Fix Flow (No Verification Attempted)
When `verificationStatus` is `FAILED`, `NOT_EXECUTABLE`, or fix has `couldAutoApplyTasks != true` with no verification:
- Analyze fix content (`suggestedFix`, `suggestedFixReasoning`, `taskOutputSummary`)
- If fix looks correct → apply via MCP
- If fix needs enhancement → use Apply Locally + Enhance Flow above
- If fix is wrong → reject via MCP, fix from scratch, commit, push
### Auto-Apply Eligibility
The `couldAutoApplyTasks` field indicates whether the fix is eligible for automatic application:
- **`true`**: Fix is eligible for auto-apply. Subagent keeps polling while verification is in progress. Returns `fix_auto_applying` when verified, or `fix_available` if verification fails.
- **`false`** or **`null`**: Fix requires manual action (apply via MCP, apply locally, or reject)
**Key point**: When subagent returns `fix_auto_applying`, do NOT call MCP to apply - self-healing handles it. Just spawn a new subagent in wait mode.
### Apply vs Reject vs Apply Locally
- **Apply via MCP**: Calls `update_self_healing_fix({ shortLink, action: "APPLY" })`. Self-healing agent applies the fix in CI and a new CIPE spawns automatically. No local git operations needed.
- **Apply Locally**: Runs `nx apply-locally <shortLink>`. Applies the patch to your local working directory and sets state to `APPLIED_LOCALLY`. Use this when you want to enhance the fix before pushing.
- **Reject via MCP**: Calls `update_self_healing_fix({ shortLink, action: "REJECT" })`. Marks fix as rejected. Use only when the fix is completely wrong and you'll fix from scratch.
### Apply Locally + Enhance Flow
When the fix needs enhancement (use `nx apply-locally`, NOT reject):
1. Apply the patch locally: `nx apply-locally <shortLink>` (this also updates state to `APPLIED_LOCALLY`)
2. Make additional changes as needed
3. Commit and push:
```bash
git add -A
git commit -m "fix: resolve <failedTaskIds>"
git push origin $(git branch --show-current)
```
4. Loop to poll for new CIPE
### Reject + Fix From Scratch Flow
When the fix is completely wrong:
1. Call MCP to reject: `update_self_healing_fix({ shortLink, action: "REJECT" })`
2. Fix the issue from scratch locally
3. Commit and push:
```bash
git add -A
git commit -m "fix: resolve <failedTaskIds>"
git push origin $(git branch --show-current)
```
4. Loop to poll for new CIPE
### Environment Issue Handling
When `failureClassification == 'ENVIRONMENT_STATE'`:
1. Call MCP to request rerun: `update_self_healing_fix({ shortLink, action: "RERUN_ENVIRONMENT_STATE" })`
2. New CIPE spawns automatically (no local git operations needed)
3. Loop to poll for new CIPE with `previousCipeUrl` set
### No-New-CIPE Handling
When `status == 'no_new_cipe'`:
This means the expected CIPE was never created - CI likely failed before Nx tasks could run.
1. **Report to user:**
```
[ci-monitor] No CI attempt for <sha> after 10 min. Check CI provider for pre-Nx failures (install, checkout, auth). Last CI attempt: <previousCipeUrl>
```
2. **If user configured auto-fix attempts** (e.g., `--auto-fix-workflow`):
- Detect package manager: check for `pnpm-lock.yaml`, `yarn.lock`, `package-lock.json`
- Run install to update lockfile:
```bash
pnpm install # or npm install / yarn install
```
- If lockfile changed:
```bash
git add pnpm-lock.yaml # or appropriate lockfile
git commit -m "chore: update lockfile"
git push origin $(git branch --show-current)
```
- Record new commit SHA, loop to poll with `expectedCommitSha`
3. **Otherwise:** Exit with `no_new_cipe` status, providing guidance for user to investigate
## Exit Conditions
Exit the monitoring loop when ANY of these conditions are met:
| Condition | Exit Type |
| ------------------------------------------- | ---------------- |
| CI passes (`cipeStatus == 'SUCCEEDED'`) | Success |
| Max CIPE cycles reached | Timeout |
| Max duration reached | Timeout |
| 3 consecutive no-progress iterations | Circuit breaker |
| No fix available and local fix not possible | Failure |
| No new CIPE and auto-fix not configured | Pre-CIPE failure |
| User cancels | Cancelled |
## Main Loop
### Step 1: Initialize Tracking
```
cycle_count = 0
start_time = now()
no_progress_count = 0
local_verify_count = 0
last_state = null
last_cipe_url = null
expected_commit_sha = null
```
### Step 2: Spawn Subagent
Spawn the `ci-watcher` subagent to poll CI status:
**Fresh start (first spawn, no expected CIPE):**
```
Task(
agent: "ci-watcher",
prompt: "Monitor CI for branch '<branch>'.
Subagent timeout: <subagent-timeout> minutes.
New-CIPE timeout: <new-cipe-timeout> minutes.
Verbosity: <verbosity>."
)
```
**After action that triggers new CIPE (wait mode):**
```
Task(
agent: "ci-watcher",
prompt: "Monitor CI for branch '<branch>'.
Subagent timeout: <subagent-timeout> minutes.
New-CIPE timeout: <new-cipe-timeout> minutes.
Verbosity: <verbosity>.
WAIT MODE: A new CIPE should spawn. Ignore old CIPE until new one appears.
Expected commit SHA: <expected_commit_sha>
Previous CIPE URL: <last_cipe_url>"
)
```
### Step 3: Handle Subagent Response
When subagent returns:
1. Check the returned status
2. Look up default behavior in the table above
3. Check if user instructions override the default
4. Execute the appropriate action
5. **If action expects new CIPE**, update tracking (see Step 3a)
6. If action results in looping, go to Step 2
### Step 3a: Track State for New-CIPE Detection
After actions that should trigger a new CIPE, record state before looping:
| Action | What to Track | Subagent Mode |
| ----------------------------- | --------------------------------------------- | ------------- |
| Fix auto-applying | `last_cipe_url = current cipeUrl` | Wait mode |
| Apply via MCP | `last_cipe_url = current cipeUrl` | Wait mode |
| Apply locally + push | `expected_commit_sha = $(git rev-parse HEAD)` | Wait mode |
| Reject + fix + push | `expected_commit_sha = $(git rev-parse HEAD)` | Wait mode |
| Fix failed + local fix + push | `expected_commit_sha = $(git rev-parse HEAD)` | Wait mode |
| No fix + local fix + push | `expected_commit_sha = $(git rev-parse HEAD)` | Wait mode |
| Environment rerun | `last_cipe_url = current cipeUrl` | Wait mode |
| No-new-CIPE + auto-fix + push | `expected_commit_sha = $(git rev-parse HEAD)` | Wait mode |
**CRITICAL**: When passing `expectedCommitSha` or `last_cipe_url` to the subagent, it enters **wait mode**:
- Subagent will **completely ignore** the old/stale CIPE
- Subagent will only wait for new CIPE to appear
- Subagent will NOT return to main agent with stale CIPE data
- Once new CIPE detected, subagent switches to normal polling
**Why wait mode matters for context preservation**: Stale CIPE data can be very large (task output summaries, suggested fix patches, reasoning). If subagent returns this to main agent, it pollutes main agent's context with useless data since we already processed that CIPE. Wait mode keeps stale data in the subagent, never sending it to main agent.
### Step 4: Progress Tracking
After each action:
- If state changed significantly → reset `no_progress_count = 0`
- If state unchanged → `no_progress_count++`
- On new CI attempt detected → reset `local_verify_count = 0`
## Status Reporting
Based on verbosity level:
| Level | What to Report |
| --------- | -------------------------------------------------------------------------- |
| `minimal` | Only final result (success/failure/timeout) |
| `medium` | State changes + periodic updates ("Cycle N \| Elapsed: Xm \| Status: ...") |
| `verbose` | All of medium + full subagent responses, git outputs, MCP responses |
## User Instruction Examples
Users can override default behaviors:
| Instruction | Effect |
| ------------------------------------------------ | --------------------------------------------- |
| "never auto-apply" | Always prompt before applying any fix |
| "always ask before git push" | Prompt before each push |
| "reject any fix for e2e tasks" | Auto-reject if `failedTaskIds` contains e2e |
| "apply all fixes regardless of verification" | Skip verification check, apply everything |
| "if confidence < 70, reject" | Check confidence field before applying |
| "run 'nx affected -t typecheck' before applying" | Add local verification step |
| "auto-fix workflow failures" | Attempt lockfile updates on pre-CIPE failures |
| "wait 45 min for new CIPE" | Override new-CIPE timeout (default: 10 min) |
## Error Handling
| Error | Action |
| ------------------------ | ------------------------------------------------------------------------------------- |
| Git rebase conflict | Report to user, exit |
| `nx apply-locally` fails | Report to user, attempt manual patch or exit |
| MCP tool error | Retry once, if fails report to user |
| Subagent spawn failure | Retry once, if fails exit with error |
| No new CIPE detected | If `--auto-fix-workflow`, try lockfile update; otherwise report to user with guidance |
| Lockfile auto-fix fails | Report to user, exit with guidance to check CI logs |
## Example Session
### Example 1: Normal Flow with Self-Healing (medium verbosity)
```
[ci-monitor] Starting CI monitor for branch 'feature/add-auth'
[ci-monitor] Config: max-cycles=5, timeout=120m, verbosity=medium
[ci-monitor] Spawning subagent to poll CI status...
[CI Monitor] CI attempt: IN_PROGRESS | Self-Healing: NOT_STARTED | Elapsed: 1m
[CI Monitor] CI attempt: FAILED | Self-Healing: IN_PROGRESS | Elapsed: 3m
[CI Monitor] CI attempt: FAILED | Self-Healing: COMPLETED | Elapsed: 5m
[ci-monitor] Fix available! Verification: COMPLETED
[ci-monitor] Applying fix via MCP...
[ci-monitor] Fix applied in CI. Waiting for new CI attempt...
[ci-monitor] Spawning subagent to poll CI status...
[CI Monitor] New CI attempt detected!
[CI Monitor] CI attempt: SUCCEEDED | Elapsed: 8m
[ci-monitor] CI passed successfully!
[ci-monitor] Summary:
- Total cycles: 2
- Total time: 12m 34s
- Fixes applied: 1
- Result: SUCCESS
```
### Example 2: Pre-CI Failure (medium verbosity)
```
[ci-monitor] Starting CI monitor for branch 'feature/add-products'
[ci-monitor] Config: max-cycles=5, timeout=120m, auto-fix-workflow=true
[ci-monitor] Spawning subagent to poll CI status...
[CI Monitor] CI attempt: FAILED | Self-Healing: COMPLETED | Elapsed: 2m
[ci-monitor] Applying fix locally, enhancing, and pushing...
[ci-monitor] Committed: abc1234
[ci-monitor] Spawning subagent to poll CI status...
[CI Monitor] Waiting for new CI attempt... (expected SHA: abc1234)
[CI Monitor] ⚠️ CI attempt timeout (10 min). Returning no_new_cipe.
[ci-monitor] Status: no_new_cipe
[ci-monitor] --auto-fix-workflow enabled. Attempting lockfile update...
[ci-monitor] Lockfile updated. Committed: def5678
[ci-monitor] Spawning subagent to poll CI status...
[CI Monitor] New CI attempt detected!
[CI Monitor] CI attempt: SUCCEEDED | Elapsed: 18m
[ci-monitor] CI passed successfully!
[ci-monitor] Summary:
- Total cycles: 3
- Total time: 22m 15s
- Fixes applied: 1 (self-healing) + 1 (lockfile)
- Result: SUCCESS
```
+437
View File
@@ -0,0 +1,437 @@
---
name: ci-monitor
description: Monitor Nx Cloud CI pipeline and handle self-healing fixes automatically. Checks for Nx Cloud connection before starting.
---
# CI Monitor Command
You are the orchestrator for monitoring Nx Cloud CI pipeline executions and handling self-healing fixes. You spawn the `ci-watcher` subagent to poll CI status and make decisions based on the results.
## Context
- **Current Branch:** !`git branch --show-current`
- **Current Commit:** !`git rev-parse --short HEAD`
- **Remote Status:** !`git status -sb | head -1`
## User Instructions
$ARGUMENTS
**Important:** If user provides specific instructions, respect them over default behaviors described below.
## Configuration Defaults
| Setting | Default | Description |
| ------------------------- | ------------- | ------------------------------------------------------------------- |
| `--max-cycles` | 10 | Maximum CIPE cycles before timeout |
| `--timeout` | 120 | Maximum duration in minutes |
| `--verbosity` | medium | Output level: minimal, medium, verbose |
| `--branch` | (auto-detect) | Branch to monitor |
| `--subagent-timeout` | 60 | Subagent polling timeout in minutes |
| `--fresh` | false | Ignore previous context, start fresh |
| `--auto-fix-workflow` | false | Attempt common fixes for pre-CIPE failures (e.g., lockfile updates) |
| `--new-cipe-timeout` | 10 | Minutes to wait for new CIPE after action |
| `--local-verify-attempts` | 3 | Max local verification + enhance cycles before pushing to CI |
Parse any overrides from `$ARGUMENTS` and merge with defaults.
## Nx Cloud Connection Check
**CRITICAL**: Before starting the monitoring loop, verify the workspace is connected to Nx Cloud.
### Step 0: Verify Nx Cloud Connection
1. **Check `nx.json`** at workspace root for `nxCloudId` or `nxCloudAccessToken`
2. **If `nx.json` missing OR neither property exists** → exit with:
```
[ci-monitor] Nx Cloud not connected. Unlock 70% faster CI and auto-fix broken PRs with https://nx.dev/nx-cloud
```
3. **If connected** → continue to main loop
## Session Context Behavior
**Important:** Within a Claude Code session, conversation context persists. If you Ctrl+C to interrupt the monitor and re-run `/ci-monitor`, Claude remembers the previous state and may continue from where it left off.
- **To continue monitoring:** Just re-run `/ci-monitor` (context is preserved)
- **To start fresh:** Use `/ci-monitor --fresh` to ignore previous context
- **For a completely clean slate:** Exit Claude Code and restart `claude`
## Default Behaviors by Status
The subagent returns with one of the following statuses. This table defines the **default behavior** for each status. User instructions can override any of these.
| Status | Default Behavior |
| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `ci_success` | Exit with success. Log "CI passed successfully!" |
| `fix_auto_applying` | Fix will be auto-applied by self-healing. Do NOT call MCP. Record `last_cipe_url`, spawn new subagent in wait mode to poll for new CIPE. |
| `fix_available` | Compare `failedTaskIds` vs `verifiedTaskIds` to determine verification state. See **Fix Available Decision Logic** section below. |
| `fix_failed` | Self-healing failed to generate fix. Attempt local fix based on `taskOutputSummary`. If successful → commit, push, loop. If not → exit with failure. |
| `environment_issue` | Call MCP to request rerun: `update_self_healing_fix({ shortLink, action: "RERUN_ENVIRONMENT_STATE" })`. New CIPE spawns automatically. Loop to poll for new CIPE. |
| `no_fix` | CI failed, no fix available (self-healing disabled or not executable). Attempt local fix if possible. Otherwise exit with failure. |
| `no_new_cipe` | Expected CIPE never spawned (CI workflow likely failed before Nx tasks). Report to user, attempt common fixes if configured, or exit with guidance. |
| `polling_timeout` | Subagent polling timeout reached. Exit with timeout. |
| `cipe_canceled` | CIPE was canceled. Exit with canceled status. |
| `cipe_timed_out` | CIPE timed out. Exit with timeout status. |
| `error` | Increment `no_progress_count`. If >= 3 → exit with circuit breaker. Otherwise wait 60s and loop. |
### Fix Available Decision Logic
When subagent returns `fix_available`, main agent compares `failedTaskIds` vs `verifiedTaskIds`:
#### Step 1: Categorize Tasks
1. **Verified tasks** = tasks in both `failedTaskIds` AND `verifiedTaskIds`
2. **Unverified tasks** = tasks in `failedTaskIds` but NOT in `verifiedTaskIds`
3. **E2E tasks** = unverified tasks where target contains "e2e" (task format: `<project>:<target>` or `<project>:<target>:<config>`)
4. **Verifiable tasks** = unverified tasks that are NOT e2e
#### Step 2: Determine Path
| Condition | Path |
| --------------------------------------- | ---------------------------------------- |
| No unverified tasks (all verified) | Apply via MCP |
| Unverified tasks exist, but ALL are e2e | Apply via MCP (treat as verified enough) |
| Verifiable tasks exist | Local verification flow |
#### Step 3a: Apply via MCP (fully/e2e-only verified)
- Call `update_self_healing_fix({ shortLink, action: "APPLY" })`
- Record `last_cipe_url`, spawn subagent in wait mode
#### Step 3b: Local Verification Flow
When verifiable (non-e2e) unverified tasks exist:
1. **Detect package manager:**
- `pnpm-lock.yaml` exists → `pnpm nx`
- `yarn.lock` exists → `yarn nx`
- Otherwise → `npx nx`
2. **Run verifiable tasks in parallel:**
- Spawn `general` subagents to run each task concurrently
- Each subagent runs: `<pm> nx run <taskId>`
- Collect pass/fail results from all subagents
3. **Evaluate results:**
| Result | Action |
| ------------------------- | ---------------------------- |
| ALL verifiable tasks pass | Apply via MCP |
| ANY verifiable task fails | Apply-locally + enhance flow |
4. **Apply-locally + enhance flow:**
- Run `nx apply-locally <shortLink>`
- Enhance the code to fix failing tasks
- Run failing tasks again to verify fix
- If still failing → increment `local_verify_count`, loop back to enhance
- If passing → commit and push, record `expected_commit_sha`, spawn subagent in wait mode
5. **Track attempts** (wraps step 4):
- Increment `local_verify_count` after each enhance cycle
- If `local_verify_count >= local_verify_attempts` (default: 3):
- Get code in commit-able state
- Commit and push with message indicating local verification failed
- Report to user:
```
[ci-monitor] Local verification failed after <N> attempts. Pushed to CI for final validation. Failed: <taskIds>
```
- Record `expected_commit_sha`, spawn subagent in wait mode (let CI be final judge)
#### Commit Message Format
```bash
git commit -m "fix(<projects>): <brief description>
Failed tasks: <taskId1>, <taskId2>
Local verification: passed|enhanced|failed-pushing-to-ci"
```
### Unverified Fix Flow (No Verification Attempted)
When `verificationStatus` is `FAILED`, `NOT_EXECUTABLE`, or fix has `couldAutoApplyTasks != true` with no verification:
- Analyze fix content (`suggestedFix`, `suggestedFixReasoning`, `taskOutputSummary`)
- If fix looks correct → apply via MCP
- If fix needs enhancement → use Apply Locally + Enhance Flow above
- If fix is wrong → reject via MCP, fix from scratch, commit, push
### Auto-Apply Eligibility
The `couldAutoApplyTasks` field indicates whether the fix is eligible for automatic application:
- **`true`**: Fix is eligible for auto-apply. Subagent keeps polling while verification is in progress. Returns `fix_auto_applying` when verified, or `fix_available` if verification fails.
- **`false`** or **`null`**: Fix requires manual action (apply via MCP, apply locally, or reject)
**Key point**: When subagent returns `fix_auto_applying`, do NOT call MCP to apply - self-healing handles it. Just spawn a new subagent in wait mode.
### Apply vs Reject vs Apply Locally
- **Apply via MCP**: Calls `update_self_healing_fix({ shortLink, action: "APPLY" })`. Self-healing agent applies the fix in CI and a new CIPE spawns automatically. No local git operations needed.
- **Apply Locally**: Runs `nx apply-locally <shortLink>`. Applies the patch to your local working directory and sets state to `APPLIED_LOCALLY`. Use this when you want to enhance the fix before pushing.
- **Reject via MCP**: Calls `update_self_healing_fix({ shortLink, action: "REJECT" })`. Marks fix as rejected. Use only when the fix is completely wrong and you'll fix from scratch.
### Apply Locally + Enhance Flow
When the fix needs enhancement (use `nx apply-locally`, NOT reject):
1. Apply the patch locally: `nx apply-locally <shortLink>` (this also updates state to `APPLIED_LOCALLY`)
2. Make additional changes as needed
3. Commit and push:
```bash
git add -A
git commit -m "fix: resolve <failedTaskIds>"
git push origin $(git branch --show-current)
```
4. Loop to poll for new CIPE
### Reject + Fix From Scratch Flow
When the fix is completely wrong:
1. Call MCP to reject: `update_self_healing_fix({ shortLink, action: "REJECT" })`
2. Fix the issue from scratch locally
3. Commit and push:
```bash
git add -A
git commit -m "fix: resolve <failedTaskIds>"
git push origin $(git branch --show-current)
```
4. Loop to poll for new CIPE
### Environment Issue Handling
When `failureClassification == 'ENVIRONMENT_STATE'`:
1. Call MCP to request rerun: `update_self_healing_fix({ shortLink, action: "RERUN_ENVIRONMENT_STATE" })`
2. New CIPE spawns automatically (no local git operations needed)
3. Loop to poll for new CIPE with `previousCipeUrl` set
### No-New-CIPE Handling
When `status == 'no_new_cipe'`:
This means the expected CIPE was never created - CI likely failed before Nx tasks could run.
1. **Report to user:**
```
[ci-monitor] No CI attempt for <sha> after 10 min. Check CI provider for pre-Nx failures (install, checkout, auth). Last CI attempt: <previousCipeUrl>
```
2. **If user configured auto-fix attempts** (e.g., `--auto-fix-workflow`):
- Detect package manager: check for `pnpm-lock.yaml`, `yarn.lock`, `package-lock.json`
- Run install to update lockfile:
```bash
pnpm install # or npm install / yarn install
```
- If lockfile changed:
```bash
git add pnpm-lock.yaml # or appropriate lockfile
git commit -m "chore: update lockfile"
git push origin $(git branch --show-current)
```
- Record new commit SHA, loop to poll with `expectedCommitSha`
3. **Otherwise:** Exit with `no_new_cipe` status, providing guidance for user to investigate
## Exit Conditions
Exit the monitoring loop when ANY of these conditions are met:
| Condition | Exit Type |
| ------------------------------------------- | ---------------- |
| CI passes (`cipeStatus == 'SUCCEEDED'`) | Success |
| Max CIPE cycles reached | Timeout |
| Max duration reached | Timeout |
| 3 consecutive no-progress iterations | Circuit breaker |
| No fix available and local fix not possible | Failure |
| No new CIPE and auto-fix not configured | Pre-CIPE failure |
| User cancels | Cancelled |
## Main Loop
### Step 1: Initialize Tracking
```
cycle_count = 0
start_time = now()
no_progress_count = 0
local_verify_count = 0
last_state = null
last_cipe_url = null
expected_commit_sha = null
```
### Step 2: Spawn Subagent
Spawn the `ci-watcher` subagent to poll CI status:
**Fresh start (first spawn, no expected CIPE):**
```
Task(
agent: "ci-watcher",
prompt: "Monitor CI for branch '<branch>'.
Subagent timeout: <subagent-timeout> minutes.
New-CIPE timeout: <new-cipe-timeout> minutes.
Verbosity: <verbosity>."
)
```
**After action that triggers new CIPE (wait mode):**
```
Task(
agent: "ci-watcher",
prompt: "Monitor CI for branch '<branch>'.
Subagent timeout: <subagent-timeout> minutes.
New-CIPE timeout: <new-cipe-timeout> minutes.
Verbosity: <verbosity>.
WAIT MODE: A new CIPE should spawn. Ignore old CIPE until new one appears.
Expected commit SHA: <expected_commit_sha>
Previous CIPE URL: <last_cipe_url>"
)
```
### Step 3: Handle Subagent Response
When subagent returns:
1. Check the returned status
2. Look up default behavior in the table above
3. Check if user instructions override the default
4. Execute the appropriate action
5. **If action expects new CIPE**, update tracking (see Step 3a)
6. If action results in looping, go to Step 2
### Step 3a: Track State for New-CIPE Detection
After actions that should trigger a new CIPE, record state before looping:
| Action | What to Track | Subagent Mode |
| ----------------------------- | --------------------------------------------- | ------------- |
| Fix auto-applying | `last_cipe_url = current cipeUrl` | Wait mode |
| Apply via MCP | `last_cipe_url = current cipeUrl` | Wait mode |
| Apply locally + push | `expected_commit_sha = $(git rev-parse HEAD)` | Wait mode |
| Reject + fix + push | `expected_commit_sha = $(git rev-parse HEAD)` | Wait mode |
| Fix failed + local fix + push | `expected_commit_sha = $(git rev-parse HEAD)` | Wait mode |
| No fix + local fix + push | `expected_commit_sha = $(git rev-parse HEAD)` | Wait mode |
| Environment rerun | `last_cipe_url = current cipeUrl` | Wait mode |
| No-new-CIPE + auto-fix + push | `expected_commit_sha = $(git rev-parse HEAD)` | Wait mode |
**CRITICAL**: When passing `expectedCommitSha` or `last_cipe_url` to the subagent, it enters **wait mode**:
- Subagent will **completely ignore** the old/stale CIPE
- Subagent will only wait for new CIPE to appear
- Subagent will NOT return to main agent with stale CIPE data
- Once new CIPE detected, subagent switches to normal polling
**Why wait mode matters for context preservation**: Stale CIPE data can be very large (task output summaries, suggested fix patches, reasoning). If subagent returns this to main agent, it pollutes main agent's context with useless data since we already processed that CIPE. Wait mode keeps stale data in the subagent, never sending it to main agent.
### Step 4: Progress Tracking
After each action:
- If state changed significantly → reset `no_progress_count = 0`
- If state unchanged → `no_progress_count++`
- On new CI attempt detected → reset `local_verify_count = 0`
## Status Reporting
Based on verbosity level:
| Level | What to Report |
| --------- | -------------------------------------------------------------------------- |
| `minimal` | Only final result (success/failure/timeout) |
| `medium` | State changes + periodic updates ("Cycle N \| Elapsed: Xm \| Status: ...") |
| `verbose` | All of medium + full subagent responses, git outputs, MCP responses |
## User Instruction Examples
Users can override default behaviors:
| Instruction | Effect |
| ------------------------------------------------ | --------------------------------------------- |
| "never auto-apply" | Always prompt before applying any fix |
| "always ask before git push" | Prompt before each push |
| "reject any fix for e2e tasks" | Auto-reject if `failedTaskIds` contains e2e |
| "apply all fixes regardless of verification" | Skip verification check, apply everything |
| "if confidence < 70, reject" | Check confidence field before applying |
| "run 'nx affected -t typecheck' before applying" | Add local verification step |
| "auto-fix workflow failures" | Attempt lockfile updates on pre-CIPE failures |
| "wait 45 min for new CIPE" | Override new-CIPE timeout (default: 10 min) |
## Error Handling
| Error | Action |
| ------------------------ | ------------------------------------------------------------------------------------- |
| Git rebase conflict | Report to user, exit |
| `nx apply-locally` fails | Report to user, attempt manual patch or exit |
| MCP tool error | Retry once, if fails report to user |
| Subagent spawn failure | Retry once, if fails exit with error |
| No new CIPE detected | If `--auto-fix-workflow`, try lockfile update; otherwise report to user with guidance |
| Lockfile auto-fix fails | Report to user, exit with guidance to check CI logs |
## Example Session
### Example 1: Normal Flow with Self-Healing (medium verbosity)
```
[ci-monitor] Starting CI monitor for branch 'feature/add-auth'
[ci-monitor] Config: max-cycles=5, timeout=120m, verbosity=medium
[ci-monitor] Spawning subagent to poll CI status...
[CI Monitor] CI attempt: IN_PROGRESS | Self-Healing: NOT_STARTED | Elapsed: 1m
[CI Monitor] CI attempt: FAILED | Self-Healing: IN_PROGRESS | Elapsed: 3m
[CI Monitor] CI attempt: FAILED | Self-Healing: COMPLETED | Elapsed: 5m
[ci-monitor] Fix available! Verification: COMPLETED
[ci-monitor] Applying fix via MCP...
[ci-monitor] Fix applied in CI. Waiting for new CI attempt...
[ci-monitor] Spawning subagent to poll CI status...
[CI Monitor] New CI attempt detected!
[CI Monitor] CI attempt: SUCCEEDED | Elapsed: 8m
[ci-monitor] CI passed successfully!
[ci-monitor] Summary:
- Total cycles: 2
- Total time: 12m 34s
- Fixes applied: 1
- Result: SUCCESS
```
### Example 2: Pre-CI Failure (medium verbosity)
```
[ci-monitor] Starting CI monitor for branch 'feature/add-products'
[ci-monitor] Config: max-cycles=5, timeout=120m, auto-fix-workflow=true
[ci-monitor] Spawning subagent to poll CI status...
[CI Monitor] CI attempt: FAILED | Self-Healing: COMPLETED | Elapsed: 2m
[ci-monitor] Applying fix locally, enhancing, and pushing...
[ci-monitor] Committed: abc1234
[ci-monitor] Spawning subagent to poll CI status...
[CI Monitor] Waiting for new CI attempt... (expected SHA: abc1234)
[CI Monitor] ⚠️ CI attempt timeout (10 min). Returning no_new_cipe.
[ci-monitor] Status: no_new_cipe
[ci-monitor] --auto-fix-workflow enabled. Attempting lockfile update...
[ci-monitor] Lockfile updated. Committed: def5678
[ci-monitor] Spawning subagent to poll CI status...
[CI Monitor] New CI attempt detected!
[CI Monitor] CI attempt: SUCCEEDED | Elapsed: 18m
[ci-monitor] CI passed successfully!
[ci-monitor] Summary:
- Total cycles: 3
- Total time: 22m 15s
- Fixes applied: 1 (self-healing) + 1 (lockfile)
- Result: SUCCESS
```
+228
View File
@@ -0,0 +1,228 @@
---
name: nx-generate
description: Generate code using nx generators. USE WHEN scaffolding code or transforming existing code - for example creating libraries or applications, or anything else that is boilerplate code or automates repetitive tasks. ALWAYS use this first when generating code with Nx instead of calling MCP tools or running nx generate immediately.
---
# Run Nx Generator
Nx generators are powerful tools that scaffold projects, make automated code migrations or automate repetitive tasks in a monorepo. They ensure consistency across the codebase and reduce boilerplate work.
This skill applies when the user wants to:
- Create new projects like libraries or applications
- Scaffold features or boilerplate code
- Run workspace-specific or custom generators
- Do anything else that an nx generator exists for
## Generator Discovery Flow
### Step 1: List Available Generators
Use the Nx CLI to discover available generators:
- List all generators for a plugin: `npx nx list @nx/react`
- View available plugins: `npx nx list`
This includes:
- Plugin generators (e.g., `@nx/react:library`, `@nx/js:library`)
- Local workspace generators (defined in the repo's own plugins)
### Step 2: Match Generator to User Request
Based on the user's request, identify which generator(s) could fulfill their needs. Consider:
- What artifact type they want to create (library, application, etc.)
- Which framework or technology stack is relevant
- Whether they mentioned specific generator names
**IMPORTANT**: When both a local workspace generator and an external plugin generator could satisfy the request, **always prefer the local workspace generator**. Local generators are customized for the specific repo's patterns and conventions.
It's possible that the user request is something that no Nx generator exists for whatsoever. In this case, you can stop using this skill and try to help the user another way. HOWEVER, the burden of proof for this is high. Before aborting, carefully consider each and every generator that's available. Look into details for any that could be related in any way before making this decision.
## Pre-Execution Checklist
Before running any generator, complete these steps:
### 1. Fetch Generator Schema
Use the `--help` flag to understand all available options:
```bash
npx nx g @nx/react:library --help
```
Pay attention to:
- Required options that must be provided
- Optional options that may be relevant to the user's request
- Default values that might need to be overridden
### 2. Read Generator Source Code
Understanding what the generator actually does helps you:
- Know what files will be created/modified
- Understand any side effects (updating configs, installing deps, etc.)
- Identify options that might not be obvious from the schema
To find generator source code:
- For plugin generators: Use `node -e "console.log(require.resolve('@nx/<plugin>/generators.json'));"` to find the generators.json, then locate the source from there
- If that fails, read directly from `node_modules/<plugin>/generators.json`
- For local generators: They are typically in `tools/generators/` or a local plugin directory. You can search the repo for the generator name to find it.
### 2.5 Reevaluate if the generator is right
Once you have built up an understanding of what the selected generator does, reconsider: Is this the right generator to service the user request?
If not, it's okay to go back to the Generator Discovery Flow and select a different generator before proceeding. If you do, make sure to go through the entire pre-execution checklist once more.
### 3. Understand Repo Context
Before generating, examine the target area of the codebase:
- Look at similar existing artifacts (other libraries, applications, etc.)
- Identify patterns and conventions used in the repo
- Note naming conventions, file structures, and configuration patterns
- Try to match these patterns when configuring the generator
For example, if similar libraries are using a specific test runner, build tool or linter, try to match that if possible.
If projects or other artifacts are organized with a specific naming convention, try to match it.
### 4. Validate Required Options
Ensure all required options have values:
- Map the user's request to generator options
- Infer values from context where possible
- Ask the user for any critical missing information
## Execution
Keep in mind that you might have to prefix things with npx/pnpx/yarn if the user doesn't have nx installed globally.
Many generators will behave differently based on where they are executed. For example, first-party nx library generators use the cwd to determine the directory that the library should be placed in. This is highly important.
### Consider Dry-Run (Optional)
Running with `--dry-run` first is strongly encouraged but not mandatory. Use your judgment:
- For complex generators or unfamiliar territory: do a dry-run first
- For simple, well-understood generators: may proceed directly
- Dry-run shows file names and created/deleted/modified markers, but not content
- There are cases where a generator does not support dry-run (for example if it had to install an npm package) - in that case --dry-run might fail. Don't be discouraged but simply move on to running the generator for real and iterating from there.
### Running the Generator
Execute the generator with:
```bash
nx generate <generator-name> <options> --no-interactive
```
**CRITICAL**: Always include `--no-interactive` to prevent prompts that would hang the execution.
Example:
```bash
nx generate @nx/react:library --name=my-utils --no-interactive
```
### Handling Generator Failures
If the generator fails:
1. **Diagnose the error** - Read the error message carefully
2. **Identify the cause** - Missing options, invalid values, conflicts, etc.
3. **Attempt automatic fix** - Adjust options or resolve conflicts
4. **Retry** - Run the generator again with corrected options
Common failure reasons:
- Missing required options
- Invalid option values
- Conflicting with existing files
- Missing dependencies
- Generator doesn't support certain flag combinations
## Post-Generation
### 1. Modify Generated Code (If Needed)
Generators provide a starting point, but the output may need adjustment to match the user's specific requirements:
- Add or modify functionality as requested
- Adjust imports, exports, or configurations
- Integrate with existing code patterns in the repo
### 2. Format Code
Run formatting on all generated/modified files:
```bash
nx format --fix
```
Languages other than javascript/typescript might need other formatting invocations too.
### 3. Run Verification
Verify that the generated code works correctly. What this looks like will vary depending on the type of generator and the targets available.
If the generator created a new project, run its targets directly
Use your best judgement to determine what needs to be verified.
Example:
```bash
nx lint <new-project>
nx test <new-project>
nx build <new-project>
```
### 4. Handle Verification Failures
When verification fails:
**If scope is manageable** (a few lint errors, minor type issues):
- Fix the issues
- Re-run verification to confirm
**If issues are extensive** (many errors, complex problems):
- Attempt simple, obvious fixes first
- If still failing, escalate to the user with:
- Description of what was generated
- What verification is failing
- What you've attempted to fix
- Remaining issues that need user input
## Error Handling
### Generator Failures
- Check the error message for specific causes
- Verify all required options are provided
- Check for conflicts with existing files
- Ensure the generator name and options are correct
### Missing Options
- Consult the generator schema for required fields
- Infer values from context when reasonable
- Ask the user for values that cannot be inferred
## Key Principles
1. **Local generators first** - Always prefer workspace/local generators over external plugin generators when both could work
2. **Understand before running** - Read both the schema AND the source code to fully understand what will happen
3. **No prompts** - Always use `--no-interactive` to prevent hanging
4. **Generators are starting points** - Modify the output as needed to fully satisfy the user's requirements
5. **Verify changes work** - Don't just generate; ensure the code builds, lints, and tests pass
6. **Be proactive about fixes** - Don't just report errors; attempt to resolve them automatically when possible
7. **Match repo patterns** - Study existing similar code in the repo and match its conventions
+9
View File
@@ -0,0 +1,9 @@
---
name: nx-plugins
description: Find and add Nx plugins. USE WHEN user wants to discover available plugins, install a new plugin, or add support for a specific framework or technology to the workspace.
---
## Finding and Installing new plugins
- List plugins: `pnpm nx list`
- Install plugins `pnpm nx add <plugin>`. Example: `pnpm nx add @nx/react`.
+58
View File
@@ -0,0 +1,58 @@
---
name: nx-run-tasks
description: Helps with running tasks in an Nx workspace. USE WHEN the user wants to execute build, test, lint, serve, or run any other tasks defined in the workspace.
---
You can run tasks with Nx in the following way.
Keep in mind that you might have to prefix things with npx/pnpx/yarn if the user doesn't have nx installed globally. Look at the package.json or lockfile to determine which package manager is in use.
For more details on any command, run it with `--help` (e.g. `nx run-many --help`, `nx affected --help`).
## Understand which tasks can be run
You can check those via `nx show project <projectname> --json`, for example `nx show project myapp --json`. It contains a `targets` section which has information about targets that can be run. You can also just look at the `package.json` scripts or `project.json` targets, but you might miss out on inferred tasks by Nx plugins.
## Run a single task
```
nx run <project>:<task>
```
where `project` is the project name defined in `package.json` or `project.json` (if present).
## Run multiple tasks
```
nx run-many -t build test lint typecheck
```
You can pass a `-p` flag to filter to specific projects, otherwise it runs on all projects. You can also use `--exclude` to exclude projects, and `--parallel` to control the number of parallel processes (default is 3).
Examples:
- `nx run-many -t test -p proj1 proj2` — test specific projects
- `nx run-many -t test --projects=*-app --exclude=excluded-app` — test projects matching a pattern
- `nx run-many -t test --projects=tag:api-*` — test projects by tag
## Run tasks for affected projects
Use `nx affected` to only run tasks on projects that have been changed and projects that depend on changed projects. This is especially useful in CI and for large workspaces.
```
nx affected -t build test lint
```
By default it compares against the base branch. You can customize this:
- `nx affected -t test --base=main --head=HEAD` — compare against a specific base and head
- `nx affected -t test --files=libs/mylib/src/index.ts` — specify changed files directly
## Useful flags
These flags work with `run`, `run-many`, and `affected`:
- `--skipNxCache` — rerun tasks even when results are cached
- `--verbose` — print additional information such as stack traces
- `--nxBail` — stop execution after the first failed task
- `--configuration=<name>` — use a specific configuration (e.g. `production`)
+186
View File
@@ -0,0 +1,186 @@
---
name: nx-workspace
description: "Explore and understand Nx workspaces. USE WHEN answering any questions about the nx workspace, the projects in it or tasks to run. EXAMPLES: 'What projects are in this workspace?', 'How is project X configured?', 'What targets can I run?', 'What's affected by my changes?', 'Which projects depend on library Y?', or any questions about Nx workspace structure, project configuration, or available tasks."
---
# Nx Workspace Exploration
This skill provides read-only exploration of Nx workspaces. Use it to understand workspace structure, project configuration, available targets, and dependencies.
Keep in mind that you might have to prefix commands with `npx`/`pnpx`/`yarn` if nx isn't installed globally. Check the lockfile to determine the package manager in use.
## Listing Projects
Use `nx show projects` to list projects in the workspace.
```bash
# List all projects
nx show projects
# Filter by pattern (glob)
nx show projects --projects "apps/*"
nx show projects --projects "shared-*"
# Filter by project type
nx show projects --type app
nx show projects --type lib
nx show projects --type e2e
# Filter by target (projects that have a specific target)
nx show projects --withTarget build
nx show projects --withTarget e2e
# Find affected projects (changed since base branch)
nx show projects --affected
nx show projects --affected --base=main
nx show projects --affected --type app
# Combine filters
nx show projects --type lib --withTarget test
nx show projects --affected --exclude="*-e2e"
# Output as JSON
nx show projects --json
```
## Project Configuration
Use `nx show project <name> --json` to get the full resolved configuration for a project.
**Important**: Do NOT read `project.json` directly - it only contains partial configuration. The `nx show project` command returns the full resolved config including inferred targets from plugins.
You can read the full project schema at `node_modules/nx/schemas/project-schema.json` to understand nx project configuration options.
```bash
# Get full project configuration
nx show project my-app --json
# Extract specific parts from the JSON
nx show project my-app --json | jq '.targets'
nx show project my-app --json | jq '.targets.build'
nx show project my-app --json | jq '.targets | keys'
# Check project metadata
nx show project my-app --json | jq '{name, root, sourceRoot, projectType, tags}'
```
## Target Information
Targets define what tasks can be run on a project.
```bash
# List all targets for a project
nx show project my-app --json | jq '.targets | keys'
# Get full target configuration
nx show project my-app --json | jq '.targets.build'
# Check target executor/command
nx show project my-app --json | jq '.targets.build.executor'
nx show project my-app --json | jq '.targets.build.command'
# View target options
nx show project my-app --json | jq '.targets.build.options'
# Check target inputs/outputs (for caching)
nx show project my-app --json | jq '.targets.build.inputs'
nx show project my-app --json | jq '.targets.build.outputs'
# Find projects with a specific target
nx show projects --withTarget serve
nx show projects --withTarget e2e
```
## Workspace Configuration
Read `nx.json` directly for workspace-level configuration.
You can read the full project schema at `node_modules/nx/schemas/nx-schema.json` to understand nx project configuration options.
```bash
# Read the full nx.json
cat nx.json
# Or use jq for specific sections
cat nx.json | jq '.targetDefaults'
cat nx.json | jq '.namedInputs'
cat nx.json | jq '.plugins'
cat nx.json | jq '.generators'
```
Key nx.json sections:
- `targetDefaults` - Default configuration applied to all targets of a given name
- `namedInputs` - Reusable input definitions for caching
- `plugins` - Nx plugins and their configuration
- ...and much more, read the schema or nx.json for details
## Affected Projects
Find projects affected by changes in the current branch.
```bash
# Affected since base branch (auto-detected)
nx show projects --affected
# Affected with explicit base
nx show projects --affected --base=main
nx show projects --affected --base=origin/main
# Affected between two commits
nx show projects --affected --base=abc123 --head=def456
# Affected apps only
nx show projects --affected --type app
# Affected excluding e2e projects
nx show projects --affected --exclude="*-e2e"
# Affected by uncommitted changes
nx show projects --affected --uncommitted
# Affected by untracked files
nx show projects --affected --untracked
```
## Common Exploration Patterns
### "What's in this workspace?"
```bash
nx show projects
nx show projects --type app
nx show projects --type lib
```
### "How do I build/test/lint project X?"
```bash
nx show project X --json | jq '.targets | keys'
nx show project X --json | jq '.targets.build'
```
### "What depends on library Y?"
```bash
# Find projects that may depend on Y by searching for imports
# (Nx doesn't have a direct "dependents" command via CLI)
grep -r "from '@myorg/Y'" --include="*.ts" --include="*.tsx" apps/ libs/
```
### "What configuration options are available?"
```bash
cat node_modules/nx/schemas/nx-schema.json | jq '.properties | keys'
cat node_modules/nx/schemas/project-schema.json | jq '.properties | keys'
```
### "Why is project X affected?"
```bash
# Check what files changed
git diff --name-only main
# See which project owns those files
nx show project X --json | jq '.root'
```
+92
View File
@@ -0,0 +1,92 @@
name: Banner Content Monitor
on:
schedule:
- cron: '*/15 * * * *'
workflow_dispatch: # Allow manual trigger
permissions: {}
env:
BANNER_URL: ${{ vars.BANNER_URL }}
jobs:
check-and-deploy:
if: ${{ github.repository_owner == 'nrwl' }}
runs-on: ubuntu-latest
steps:
- name: Fetch banner content and compute hash
id: banner
run: |
if [ -z "$BANNER_URL" ]; then
echo "BANNER_URL is not set"
exit 1
fi
# Fetch content and compute hash
CONTENT_HASH=$(curl -sf "$BANNER_URL" | sha256sum | cut -d' ' -f1)
if [ -z "$CONTENT_HASH" ]; then
echo "Failed to fetch banner content"
exit 1
fi
echo "current_hash=$CONTENT_HASH" >> $GITHUB_OUTPUT
echo "Current banner hash: $CONTENT_HASH"
- name: Restore cached hash
id: cache
uses: actions/cache/restore@v4
with:
path: .banner-hash
key: banner-content-hash-
restore-keys: |
banner-content-hash-
- name: Compare hashes
id: compare
run: |
CURRENT_HASH="${{ steps.banner.outputs.current_hash }}"
if [ -f .banner-hash ]; then
CACHED_HASH=$(cat .banner-hash)
echo "Cached hash: $CACHED_HASH"
else
CACHED_HASH=""
echo "No cached hash found"
fi
if [ "$CURRENT_HASH" != "$CACHED_HASH" ]; then
echo "changed=true" >> $GITHUB_OUTPUT
echo "Banner content has changed!"
else
echo "changed=false" >> $GITHUB_OUTPUT
echo "Banner content unchanged"
fi
- name: Trigger Netlify deploys
if: steps.compare.outputs.changed == 'true'
env:
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
run: |
npm install -g netlify-cli
echo "Triggering nx-docs deploy..."
netlify deploy --trigger --prod -s nx-docs
echo "Triggering nx-dev deploy..."
netlify deploy --trigger --prod -s nx-dev
echo "Both deploys triggered successfully"
- name: Save new hash to cache
if: steps.compare.outputs.changed == 'true'
run: |
echo "${{ steps.banner.outputs.current_hash }}" > .banner-hash
- name: Update cache
if: steps.compare.outputs.changed == 'true'
uses: actions/cache/save@v4
with:
path: .banner-hash
key: banner-content-hash-${{ github.run_id }}
+7 -3
View File
@@ -18,6 +18,7 @@ jobs:
main-linux:
runs-on: ubuntu-latest
env:
NX_BATCH_MODE: 'true'
NX_E2E_CI_CACHE_KEY: e2e-github-linux
NX_DAEMON: 'true'
NX_PERF_LOGGING: 'false'
@@ -29,6 +30,9 @@ jobs:
NX_ALLOW_NON_CACHEABLE_DTE: 'true'
NX_CLOUD_USE_NEW_TASK_APIS: 'true'
NX_CLOUD_USE_NEW_STREAM_OUTPUT: 'true'
NX_CLOUD_EXPERIMENTAL_POLLING: 'true'
NX_CLOUD_CONTINUOUS_ASSIGNMENT: 'false'
NX_CLOUD_VERBOSE_LOGGING: 'true'
steps:
- name: Checkout
@@ -47,7 +51,7 @@ jobs:
main-branch-name: 'master'
- name: Start CI Run
run: npx nx-cloud@next start-ci-run --auto-apply-fixes="*format:check*,*sync:check*,*conformance:check*,*format-native*,*lint-native*,*lint*,*astro-docs:validate-links*" --distribute-on="./.nx/workflows/dynamic-changesets.yaml" --stop-agents-after="e2e"
run: npx nx-cloud@next start-ci-run --distribute-on="./.nx/workflows/dynamic-changesets.yaml" --stop-agents-after="e2e"
- name: Install dependencies
run: |
@@ -74,7 +78,7 @@ jobs:
pnpm playwright install --with-deps
- name: Nx Report
run:
run:
pnpm nx report
- name: Run Checks/Lint/Test/Build
@@ -93,7 +97,7 @@ jobs:
pnpm nx run-many -t check-imports check-lock-files check-codeowners --parallel=1 --no-dte &
pids+=($!)
pnpm nx affected --targets=lint,test,test-kt,build,e2e,e2e-ci,format-native,lint-native &
pnpm nx affected --targets=lint,test,build,e2e,e2e-ci,format-native,lint-native,gradle:build-ci &
pids+=($!)
for pid in "${pids[@]}"; do
+1 -1
View File
@@ -26,7 +26,7 @@ jobs:
uses: pnpm/action-setup@7088e561eb65bb68695d245aa206f005ef30921d # v4.1.0
id: pnpm-install
with:
version: 10.11.1
version: 10.28.2
run_install: false
- name: Get pnpm store directory
+1 -1
View File
@@ -20,7 +20,7 @@ jobs:
- uses: pnpm/action-setup@7088e561eb65bb68695d245aa206f005ef30921d # v4.1.0
with:
version: 10.11.1
version: 10.28.2
- name: Use Node.js ${{ matrix.node_version }}
uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0
+1 -1
View File
@@ -75,7 +75,7 @@ const matrixData: MatrixData = {
os_name: 'Linux',
os_timeout: 60,
package_managers: ['npm', 'pnpm', 'yarn'],
node_versions: ['20.19.0', '22.12.0', '24.0.0'],
node_versions: ['20.19.0', '22.13.0', '24.0.0'],
excluded: ['e2e-detox', 'e2e-react-native', 'e2e-expo']
},
// Docker is not supported on ARM-based macOS runners (no nested virtualization)
+1 -1
View File
@@ -18,7 +18,7 @@ jobs:
- uses: pnpm/action-setup@7088e561eb65bb68695d245aa206f005ef30921d # v4.1.0
with:
version: 10.11.1 # Aligned with root package.json (pnpm/action-setup will helpfully error if out of sync)
version: 10.28.2 # Aligned with root package.json (pnpm/action-setup will helpfully error if out of sync)
- name: Run a security audit
run: pnpm dlx audit-ci --critical --report-type summary
+49 -10
View File
@@ -22,7 +22,7 @@ env:
NX_RUN_GROUP: ${{ github.run_id }}-${{ github.run_attempt }}
CYPRESS_INSTALL_BINARY: 0
NODE_VERSION: 22.16.0
PNPM_VERSION: 10.11.1 # Aligned with root package.json (pnpm/action-setup will helpfully error if out of sync)
PNPM_VERSION: 10.28.2 # Aligned with root package.json (pnpm/action-setup will helpfully error if out of sync)
jobs:
# We first need to determine the version we are releasing, and if we need a custom repo or ref to use for the git checkout in subsequent steps.
@@ -174,7 +174,7 @@ jobs:
bash -c "
set -e
echo 'https://dl-cdn.alpinelinux.org/alpine/edge/community' >> /etc/apk/repositories
apk add --no-cache curl xz openjdk21
apk add --no-cache curl xz openjdk21 build-base lld
# Set up Java 21
export JAVA_HOME=/usr/lib/jvm/java-21-openjdk
@@ -194,6 +194,10 @@ jobs:
npm i -g pnpm@${PNPM_VERSION} --force
pnpm --version
# Help clang find GCC runtime (crtbeginS.o, libgcc) and use lld for jemalloc build
GCC_DIR=\$(dirname \$(find /usr/lib/gcc -name crtbeginS.o | head -1))
export CFLAGS=\"\${CFLAGS} -fuse-ld=lld --gcc-install-dir=\${GCC_DIR}\"
# Install deps and run native build
pnpm install --frozen-lockfile
rustup target add x86_64-unknown-linux-musl
@@ -230,6 +234,9 @@ jobs:
node --version
npm --version
# Help clang find GCC runtime (crtbeginS.o, libgcc) and use lld for jemalloc build
export CFLAGS="${CFLAGS} -fuse-ld=lld --gcc-toolchain=/usr/aarch64-unknown-linux-gnu"
npm i -g pnpm@${PNPM_VERSION} --force
pnpm --version
@@ -260,7 +267,7 @@ jobs:
bash -c "
set -e
echo 'https://dl-cdn.alpinelinux.org/alpine/edge/community' >> /etc/apk/repositories
apk add --no-cache curl xz openjdk21
apk add --no-cache curl xz openjdk21 build-base lld
# Set up Java 21
export JAVA_HOME=/usr/lib/jvm/java-21-openjdk
@@ -280,6 +287,10 @@ jobs:
npm i -g pnpm@${PNPM_VERSION} --force
pnpm --version
# Help clang find GCC runtime (crtbeginS.o, libgcc) and use lld for jemalloc build
GCC_DIR=\$(dirname \$(find /aarch64-linux-musl-cross/lib/gcc -name crtbeginS.o | head -1))
export CFLAGS=\"\${CFLAGS} -fuse-ld=lld --gcc-install-dir=\${GCC_DIR}\"
# Install deps and run native build
pnpm install --frozen-lockfile
rustup target add aarch64-unknown-linux-musl
@@ -354,12 +365,24 @@ jobs:
architecture: x86
- name: Build in docker
uses: addnab/docker-run-action@4f65fabd2431ebc8d299f8e5a018d79a769ae185 # v3
if: ${{ matrix.settings.docker }}
with:
image: ${{ matrix.settings.docker }}
options: --user 0:0 -v ${{ github.workspace }}/.cargo-cache/git/db:/usr/local/cargo/git/db -v ${{ github.workspace }}/.cargo/registry/cache:/usr/local/cargo/registry/cache -v ${{ github.workspace }}/.cargo/registry/index:/usr/local/cargo/registry/index -v ${{ github.workspace }}:/build -w /build
run: ${{ matrix.settings.build }}
shell: bash
env:
BUILD_SCRIPT: ${{ matrix.settings.build }}
run: |
SCRIPT_FILE=$(mktemp)
echo "$BUILD_SCRIPT" > "$SCRIPT_FILE"
docker run --rm \
--user 0:0 \
-e PNPM_VERSION \
-v ${{ github.workspace }}/.cargo-cache/git/db:/usr/local/cargo/git/db \
-v ${{ github.workspace }}/.cargo/registry/cache:/usr/local/cargo/registry/cache \
-v ${{ github.workspace }}/.cargo/registry/index:/usr/local/cargo/registry/index \
-v ${{ github.workspace }}:/build \
-v "$SCRIPT_FILE:/build-script.sh" \
-w /build \
${{ matrix.settings.docker }} \
bash /build-script.sh
- name: Build
run: ${{ matrix.settings.build }}
@@ -406,7 +429,7 @@ jobs:
env
whoami
sudo pkg install -y -f node libnghttp2 www/npm git openjdk17
sudo npm install --location=global --ignore-scripts pnpm@10.11.1
sudo npm install --location=global --ignore-scripts pnpm@10.28.2
# Set up Java 17
export JAVA_HOME=/usr/local/openjdk17
export PATH="$JAVA_HOME/bin:$PATH"
@@ -472,11 +495,27 @@ jobs:
rm -rf ~/.rustup/toolchains/*/share || true
# Remove other development tool caches
rm -rf ~/.cache/* || true
# Remove unnecessary workspace directories
rm -rf docs astro-docs nx-dev || true
echo "Checking disk space after cleanup"
df -h
echo "Building FreeBSD bindings"
pnpm nx run-many --verbose --outputStyle stream --target=build-native -- --target=x86_64-unknown-freebsd
BUILD_EXIT=0
pnpm nx run-many --verbose --outputStyle stream --target=build-native -- --target=x86_64-unknown-freebsd || BUILD_EXIT=$?
echo "=== Disk usage after build ==="
df -h
if [ "$BUILD_EXIT" -ne 0 ]; then
echo "Build failed with exit code $BUILD_EXIT"
exit $BUILD_EXIT
fi
echo "Build succeeded"
echo "Cleaning up"
pnpm nx reset
rm -rf node_modules
+2 -2
View File
@@ -29,7 +29,7 @@ jest.debug.config.js
# Banner JSON files are generated during static builds
/nx-dev/nx-dev/lib/banner.json
/astro-docs/src/content/banner.json
**/tests/temp-db
**/tests/temp-db*
# Issues scraper creates these files, stored by github's cache
/scripts/issues-scraper/cached
@@ -71,7 +71,7 @@ dependency-reduced-pom.xml
*.wasm
/wasi-sdk*
vite.config.*.timestamp*
*.config.timestamp*
storybook-static
+1
View File
@@ -8,6 +8,7 @@ common-env-vars: &common-env-vars
# These are need for build and link validation for next.js and astro apps
NEXT_PUBLIC_ASTRO_URL: 'https://master--nx-docs.netlify.app'
NX_DEV_URL: 'https://canary.nx.dev'
NX_CLOUD_IO_TRACING_DIRECTORY: '~/io-tracing'
common-init-steps: &common-init-steps
- name: Checkout
+34 -5
View File
@@ -1,5 +1,9 @@
distribute-on:
default: auto linux-large, 3 linux-extra-large
extra-small-changeset: 6 linux-large, 3 linux-extra-large
small-changeset: 6 linux-large, 4 linux-extra-large
medium-changeset: 6 linux-large, 5 linux-extra-large
large-changeset: 6 linux-large, 6 linux-extra-large
extra-large-changeset: 8 linux-large, 8 linux-extra-large
assignment-rules:
- projects:
- e2e-gradle
@@ -8,6 +12,23 @@ assignment-rules:
run-on:
- agent: linux-extra-large
parallelism: 1
- projects:
- e2e-next
- e2e-plugin
targets:
- e2e-ci**
run-on:
- agent: linux-extra-large
parallelism: 2
- projects:
- e2e-angular
- e2e-node
- e2e-react
targets:
- e2e-ci**
run-on:
- agent: linux-extra-large
parallelism: 1
- projects:
- nx
@@ -25,15 +46,14 @@ assignment-rules:
- projects:
- e2e-release
- e2e-angular
- e2e-react
- e2e-next
- e2e-nuxt
- e2e-web
- e2e-eslint
- e2e-remix
- e2e-cypress
- e2e-docker
- e2e-js
- e2e-nx
- e2e-nx-init
- e2e-dotnet
- e2e-workspace-create
@@ -44,7 +64,7 @@ assignment-rules:
- agent: linux-large
parallelism: 1
- agent: linux-extra-large
parallelism: 1
parallelism: 2
# All other e2e tests can run in parallel
- targets:
@@ -80,6 +100,15 @@ assignment-rules:
- agent: linux-extra-large
parallelism: 6
# TODO(altan): remove when scheduling issue resolved
- projects:
- nx-dev
targets:
- prebuild-banner
run-on:
- agent: linux-extra-large
parallelism: 6
- targets:
- "*"
run-on:
+7
View File
@@ -3,3 +3,10 @@ nx-dev/**/jest.config.js
_files
_solution
nx-dev/tutorial/**/templates
# Generated by napi-rs (outputs of build-native)
packages/nx/src/native/index.d.ts
packages/nx/src/native/native-bindings.js
# Workaround for ignore-files crate bug with prefix matching
**/target/
+479
View File
@@ -0,0 +1,479 @@
---
description: Polls Nx Cloud CI pipeline and self-healing status. Returns structured state when actionable. Spawned by /nx-cloud-ci-monitor command to monitor CI Attempt status.
mode: subagent
---
# CI Watcher Subagent
You are a CI monitoring subagent responsible for polling Nx Cloud CI Attempt status and self-healing state. You report status back to the main agent - you do NOT make apply/reject decisions.
## Your Responsibilities
1. Poll CI status using the `ci_information` MCP tool
2. Implement exponential backoff between polls
3. Return structured state when an actionable condition is reached
4. Track iteration count and elapsed time
5. Output status updates based on verbosity level
## Input Parameters (from Main Agent)
The main agent may provide these optional parameters in the prompt:
| Parameter | Description |
| ------------------- | -------------------------------------------------------- |
| `branch` | Branch to monitor (auto-detected if not provided) |
| `expectedCommitSha` | Commit SHA that should trigger a new CI Attempt |
| `previousCipeUrl` | CI Attempt URL before the action (to detect change) |
| `subagentTimeout` | Polling timeout in minutes (default: 60) |
| `verbosity` | Output level: minimal, medium, verbose (default: medium) |
When `expectedCommitSha` or `previousCipeUrl` is provided, you must detect whether a new CI Attempt has spawned.
## MCP Tool Reference
### `ci_information`
**Input:**
```json
{
"branch": "string (optional, defaults to current git branch)",
"select": "string (optional, comma-separated field names)",
"pageToken": "number (optional, 0-based pagination for long strings)"
}
```
**Output:**
```json
{
"cipeStatus": "NOT_STARTED | IN_PROGRESS | SUCCEEDED | FAILED | CANCELED | TIMED_OUT",
"cipeUrl": "string",
"branch": "string",
"commitSha": "string | null",
"failedTaskIds": "string[]",
"verifiedTaskIds": "string[]",
"selfHealingEnabled": "boolean",
"selfHealingStatus": "NOT_STARTED | IN_PROGRESS | COMPLETED | FAILED | NOT_EXECUTABLE | null",
"verificationStatus": "NOT_STARTED | IN_PROGRESS | COMPLETED | FAILED | NOT_EXECUTABLE | null",
"userAction": "NONE | APPLIED | REJECTED | APPLIED_LOCALLY | APPLIED_AUTOMATICALLY | null",
"failureClassification": "string | null",
"taskOutputSummary": "string | null",
"suggestedFixReasoning": "string | null",
"suggestedFixDescription": "string | null",
"suggestedFix": "string | null",
"shortLink": "string | null",
"couldAutoApplyTasks": "boolean | null",
"confidence": "number | null",
"confidenceReasoning": "string | null"
}
```
**Select Parameter:**
| Usage | Returns |
| --------------- | ----------------------------------------------------------- |
| No `select` | Formatted overview (truncated, not recommended for polling) |
| Single field | Raw value with pagination for long strings |
| Multiple fields | Object with requested field values |
**Field Sets for Efficient Polling:**
```yaml
WAIT_FIELDS:
'cipeUrl,commitSha,cipeStatus'
# Minimal fields for detecting new CI Attempt
LIGHT_FIELDS:
'cipeStatus,cipeUrl,branch,commitSha,selfHealingStatus,verificationStatus,userAction,failedTaskIds,verifiedTaskIds,selfHealingEnabled,failureClassification,couldAutoApplyTasks,shortLink,confidence,confidenceReasoning'
# Status fields for determining actionable state
HEAVY_FIELDS:
'taskOutputSummary,suggestedFix,suggestedFixReasoning,suggestedFixDescription'
# Large content fields - fetch only when returning to main agent
```
## Initial Wait
Before first poll, wait based on context:
- **Fresh start (no expected CIPE):** Wait 60 seconds to allow CI to start
- **Expecting new CIPE:** Wait 30 seconds (action already triggered)
**IMPORTANT:** Always run sleep in foreground, NOT as background command.
```bash
sleep 60 # or 30 if expecting new CIPE (FOREGROUND, not background)
```
## Two-Phase Operation
The subagent operates in one of two modes depending on input:
### Mode 1: Fresh Start (no `expectedCommitSha` or `previousCipeUrl`)
Normal polling - process whatever CIPE is returned by `ci_information`.
### Mode 2: Wait-for-New-CIPE (when `expectedCommitSha` or `previousCipeUrl` provided)
**CRITICAL**: When expecting a new CIPE, the subagent must **completely ignore** the old/stale CIPE. Do NOT process its status, do NOT return actionable states based on it.
#### Phase A: Wait Mode
1. Start a **new-CIPE timeout** timer (default: 30 minutes)
2. On each poll of `ci_information`:
- Check if CIPE is NEW:
- `cipeUrl` differs from `previousCipeUrl`**new CIPE detected**
- `commitSha` matches `expectedCommitSha`**correct CIPE detected**
- If still OLD CIPE: **ignore all status fields**, just wait and poll again
- Do NOT return `fix_available`, `ci_success`, etc. based on old CIPE!
3. Output wait status (see below)
4. If timeout (30 min) reached → return `no_new_cipe`
#### Phase B: Normal Polling (after new CIPE detected)
Once new CIPE is detected:
1. Clear the new-CIPE timeout
2. Switch to normal polling mode
3. Process the NEW CIPE's status normally
4. Return when actionable state reached
### Wait Mode Output
While in wait mode, output clearly that you're waiting (not processing):
```
[CI Monitor] ═══════════════════════════════════════════════════════
[CI Monitor] WAIT MODE - Expecting new CI Attempt
[CI Monitor] Expected SHA: <expectedCommitSha>
[CI Monitor] Previous CI Attempt: <previousCipeUrl>
[CI Monitor] ═══════════════════════════════════════════════════════
[CI Monitor] Polling... (elapsed: 0m 30s)
[CI Monitor] Still seeing previous CI Attempt (ignoring): <oldCipeUrl>
[CI Monitor] Polling... (elapsed: 1m 30s)
[CI Monitor] Still seeing previous CI Attempt (ignoring): <oldCipeUrl>
[CI Monitor] Polling... (elapsed: 2m 30s)
[CI Monitor] ✓ New CI Attempt detected! URL: <newCipeUrl>, SHA: <newCommitSha>
[CI Monitor] Switching to normal polling mode...
```
### Why This Matters (Context Preservation)
**The problem**: Stale CIPE data can be very large:
- `taskOutputSummary`: potentially thousands of characters of build/test output
- `suggestedFix`: entire patch files
- `suggestedFixReasoning`: detailed explanation
If subagent returns stale CIPE data to main agent, it **pollutes main agent's context** with useless information (we already processed that CIPE). This wastes valuable context window.
**Without wait mode:**
1. Poll `ci_information` → get old CIPE with huge data
2. Return to main agent with all that stale data
3. Main agent's context gets polluted with useless info
4. Main agent has to process/ignore it anyway
**With wait mode:**
1. Poll `ci_information` → get old CIPE → **ignore it, don't return**
2. Keep waiting internally (stale data stays in subagent)
3. New CIPE appears → switch to normal mode
4. Return to main agent with only the NEW, relevant CIPE data
## Polling Loop
### Subagent State Management
Maintain internal accumulated state across polls:
```
accumulated_state = {}
```
### Call `ci_information` MCP Tool
**Wait Mode (expecting new CI Attempt):**
```
ci_information({
branch: "<branch_name>",
select: "cipeUrl,commitSha,cipeStatus"
})
```
Only fetch minimal fields needed to detect CI Attempt change. Do NOT fetch heavy fields - stale data wastes context.
**Normal Mode (processing CI Attempt):**
```
ci_information({
branch: "<branch_name>",
select: "cipeStatus,cipeUrl,branch,commitSha,selfHealingStatus,verificationStatus,userAction,failedTaskIds,verifiedTaskIds,selfHealingEnabled,failureClassification,couldAutoApplyTasks,shortLink,confidence,confidenceReasoning"
})
```
Merge response into `accumulated_state` after each poll.
### Analyze Response
**If in Wait Mode** (expecting new CIPE):
1. Check if CIPE is new (see Two-Phase Operation above)
2. If old CIPE → **ignore status**, output wait message, poll again
3. If new CIPE → switch to normal mode, continue below
**If in Normal Mode**:
Based on the response, decide whether to **keep polling** or **return to main agent**.
### Keep Polling When
Continue polling (with backoff) if ANY of these conditions are true:
| Condition | Reason |
| --------------------------------------- | ---------------------------------------- |
| `cipeStatus == 'IN_PROGRESS'` | CI still running |
| `cipeStatus == 'NOT_STARTED'` | CI hasn't started yet |
| `selfHealingStatus == 'IN_PROGRESS'` | Self-healing agent working |
| `selfHealingStatus == 'NOT_STARTED'` | Self-healing not started yet |
| `failureClassification == 'FLAKY_TASK'` | Auto-rerun in progress |
| `userAction == 'APPLIED_AUTOMATICALLY'` | New CI Attempt spawning after auto-apply |
When `couldAutoApplyTasks == true`:
- `verificationStatus` = `NOT_STARTED`, `IN_PROGRESS` → keep polling (verification still in progress)
- `verificationStatus` = `COMPLETED` → return `fix_auto_applying` (auto-apply will happen, main agent spawns wait mode subagent)
- `verificationStatus` = `FAILED`, `NOT_EXECUTABLE` → return `fix_available` (auto-apply won't happen, needs manual action)
### Exponential Backoff
Between polls, wait with exponential backoff:
| Poll Attempt | Wait Time |
| ------------ | ----------------- |
| 1st | 60 seconds |
| 2nd | 90 seconds |
| 3rd+ | 120 seconds (cap) |
Reset to 60 seconds when state changes significantly.
**IMPORTANT:** Run sleep in foreground (NOT as background command). Background sleep causes "What should Claude do?" prompts when completed.
```bash
# Example backoff - run in FOREGROUND
sleep 60 # First wait
sleep 90 # Second wait
sleep 120 # Third and subsequent waits (capped)
```
### Fetch Heavy Fields on Actionable State
Before returning to main agent, fetch heavy fields if the status requires them:
| Status | Heavy Fields Needed |
| ------------------- | ------------------------------------------------------------------------------ |
| `ci_success` | None |
| `fix_auto_applying` | None |
| `fix_available` | `taskOutputSummary,suggestedFix,suggestedFixReasoning,suggestedFixDescription` |
| `fix_failed` | `taskOutputSummary` |
| `no_fix` | `taskOutputSummary` |
| `environment_issue` | None |
| `no_new_cipe` | None |
| `polling_timeout` | None |
| `cipe_canceled` | None |
| `cipe_timed_out` | None |
```
# Example: fetching heavy fields for fix_available
ci_information({
branch: "<branch_name>",
select: "taskOutputSummary,suggestedFix,suggestedFixReasoning,suggestedFixDescription"
})
```
Merge response into `accumulated_state`, then return merged state to main agent.
**Pagination:** Heavy string fields return first page only. If `hasMore` indicated, include in return format so main agent knows more content available.
### Return to Main Agent When
Return immediately with structured state if ANY of these conditions are true:
| Status | Condition |
| ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `ci_success` | `cipeStatus == 'SUCCEEDED'` |
| `fix_auto_applying` | `selfHealingStatus == 'COMPLETED'` AND `couldAutoApplyTasks == true` AND `verificationStatus == 'COMPLETED'` |
| `fix_available` | `selfHealingStatus == 'COMPLETED'` AND `suggestedFix != null` AND (`couldAutoApplyTasks != true` OR `verificationStatus` in (`FAILED`, `NOT_EXECUTABLE`)) |
| `fix_failed` | `selfHealingStatus == 'FAILED'` |
| `environment_issue` | `failureClassification == 'ENVIRONMENT_STATE'` |
| `no_fix` | `cipeStatus == 'FAILED'` AND (`selfHealingEnabled == false` OR `selfHealingStatus == 'NOT_EXECUTABLE'`) |
| `no_new_cipe` | `expectedCommitSha` or `previousCipeUrl` provided, but no new CI Attempt detected after 30 min |
| `polling_timeout` | Subagent has been polling for > configured timeout (default 60 min) |
| `cipe_canceled` | `cipeStatus == 'CANCELED'` |
| `cipe_timed_out` | `cipeStatus == 'TIMED_OUT'` |
## Subagent Timeout
Track elapsed time. If you have been polling for more than **60 minutes** (configurable via main agent), return with `status: polling_timeout`.
## Return Format
When returning to the main agent, provide a structured response with accumulated state:
```
## CI Monitor Result
**Status:** <status>
**Iterations:** <count>
**Elapsed:** <minutes>m <seconds>s
### CI Attempt Details
- **Status:** <cipeStatus>
- **URL:** <cipeUrl>
- **Branch:** <branch>
- **Commit:** <commitSha>
- **Failed Tasks:** <failedTaskIds>
- **Verified Tasks:** <verifiedTaskIds>
### Self-Healing Details
- **Enabled:** <selfHealingEnabled>
- **Status:** <selfHealingStatus>
- **Verification:** <verificationStatus>
- **User Action:** <userAction>
- **Classification:** <failureClassification>
- **Confidence:** <confidence>
- **Confidence Reasoning:** <confidenceReasoning>
### Fix Information (if available)
- **Short Link:** <shortLink>
- **Description:** <suggestedFixDescription>
- **Reasoning:** <suggestedFixReasoning>
### Task Output Summary (first page)
<taskOutputSummary>
[MORE_CONTENT_AVAILABLE: taskOutputSummary, pageToken: 1]
### Suggested Fix (first page)
<suggestedFix>
[MORE_CONTENT_AVAILABLE: suggestedFix, pageToken: 1]
```
### Pagination Indicators
When a heavy field has more content available, append indicator:
```
[MORE_CONTENT_AVAILABLE: <fieldName>, pageToken: <nextPage>]
```
Main agent can fetch additional pages if needed using:
```
ci_information({ select: "<fieldName>", pageToken: <nextPage> })
```
Fields that may have pagination:
- `taskOutputSummary` (reverse pagination - page 0 = most recent)
- `suggestedFix` (forward pagination - page 0 = start)
- `suggestedFixReasoning`
### Return Format for `no_new_cipe`
When returning with `status: no_new_cipe`, include additional context:
```
## CI Monitor Result
**Status:** no_new_cipe
**Iterations:** <count>
**Elapsed:** <minutes>m <seconds>s
### Expected CI Attempt Not Found
- **Expected Commit SHA:** <expectedCommitSha>
- **Previous CI Attempt URL:** <previousCipeUrl>
- **Last Seen CI Attempt URL:** <cipeUrl>
- **Last Seen Commit SHA:** <commitSha>
- **New CI Attempt Timeout:** 30 minutes (exceeded)
### Likely Cause
CI workflow failed before Nx tasks could run (e.g., install step, checkout, auth).
Check your CI provider logs for the commit <expectedCommitSha>.
### Last Known CI Attempt State
- **Status:** <cipeStatus>
- **Branch:** <branch>
```
## Status Reporting (Verbosity-Controlled)
Output is controlled by the `verbosity` parameter from the main agent:
| Level | What to Output |
| --------- | ----------------------------------------------------------------- |
| `minimal` | No intermediate output. Only return final result when actionable. |
| `medium` | Output only on significant state changes (not every poll). |
| `verbose` | Output detailed phase information after every poll. |
### Minimal Verbosity
No output during polling. Poll silently and return when done.
### Medium Verbosity (Default)
Output **only when state changes significantly** to save context tokens:
- `cipeStatus` changes (e.g., IN_PROGRESS → FAILED)
- `selfHealingStatus` changes (e.g., IN_PROGRESS → COMPLETED)
- New CI Attempt detected (in wait mode)
Format: single line, no decorators:
```
[CI Monitor] CI: FAILED | Self-Healing: IN_PROGRESS | Elapsed: 4m
```
### Verbose Verbosity
Output detailed phase box after every poll:
```
[CI Monitor] ─────────────────────────────────────────────────────
[CI Monitor] Iteration <N> | Elapsed: <X>m <Y>s
[CI Monitor]
[CI Monitor] CI Status: <cipeStatus>
[CI Monitor] Self-Healing: <selfHealingStatus>
[CI Monitor] Verification: <verificationStatus>
[CI Monitor] Classification: <failureClassification>
[CI Monitor]
[CI Monitor] → <human-readable phase description>
[CI Monitor] ─────────────────────────────────────────────────────
```
### Phase Descriptions (for verbose output)
| Status Combo | Description |
| ----------------------------------------------------------------------------------------- | ------------------------------------------- |
| `cipeStatus: IN_PROGRESS` | "CI running..." |
| `cipeStatus: NOT_STARTED` | "Waiting for CI to start..." |
| `cipeStatus: FAILED` + `selfHealingStatus: NOT_STARTED` | "CI failed. Self-healing starting..." |
| `cipeStatus: FAILED` + `selfHealingStatus: IN_PROGRESS` | "CI failed. Self-healing generating fix..." |
| `cipeStatus: FAILED` + `selfHealingStatus: COMPLETED` + `verificationStatus: IN_PROGRESS` | "Fix generated! Verification running..." |
| `cipeStatus: FAILED` + `selfHealingStatus: COMPLETED` + `verificationStatus: COMPLETED` | "Fix ready! Verified successfully." |
| `cipeStatus: FAILED` + `selfHealingStatus: COMPLETED` + `verificationStatus: FAILED` | "Fix generated but verification failed." |
| `cipeStatus: FAILED` + `selfHealingStatus: FAILED` | "Self-healing could not generate a fix." |
| `cipeStatus: SUCCEEDED` | "CI passed!" |
## Important Notes
- You do NOT make apply/reject decisions - that's the main agent's job
- You do NOT perform git operations
- You only poll and report state
- Respect the `verbosity` parameter for output (default: medium)
- If `ci_information` returns an error, wait and retry (count as failed poll)
- Track consecutive failures - if 5 consecutive failures, return with `status: error`
- When expecting new CI Attempt, track the 30-minute new-CI-Attempt timeout separately from the main polling timeout
+437
View File
@@ -0,0 +1,437 @@
---
description: Monitor Nx Cloud CI pipeline and handle self-healing fixes automatically. Checks for Nx Cloud connection before starting.
argument-hint: '[instructions] [--max-cycles N] [--timeout MINUTES] [--verbosity minimal|medium|verbose] [--branch BRANCH] [--fresh] [--auto-fix-workflow] [--new-cipe-timeout MINUTES]'
---
# CI Monitor Command
You are the orchestrator for monitoring Nx Cloud CI pipeline executions and handling self-healing fixes. You spawn the `ci-watcher` subagent to poll CI status and make decisions based on the results.
## Context
- **Current Branch:** !`git branch --show-current`
- **Current Commit:** !`git rev-parse --short HEAD`
- **Remote Status:** !`git status -sb | head -1`
## User Instructions
$ARGUMENTS
**Important:** If user provides specific instructions, respect them over default behaviors described below.
## Configuration Defaults
| Setting | Default | Description |
| ------------------------- | ------------- | ------------------------------------------------------------------- |
| `--max-cycles` | 10 | Maximum CIPE cycles before timeout |
| `--timeout` | 120 | Maximum duration in minutes |
| `--verbosity` | medium | Output level: minimal, medium, verbose |
| `--branch` | (auto-detect) | Branch to monitor |
| `--subagent-timeout` | 60 | Subagent polling timeout in minutes |
| `--fresh` | false | Ignore previous context, start fresh |
| `--auto-fix-workflow` | false | Attempt common fixes for pre-CIPE failures (e.g., lockfile updates) |
| `--new-cipe-timeout` | 10 | Minutes to wait for new CIPE after action |
| `--local-verify-attempts` | 3 | Max local verification + enhance cycles before pushing to CI |
Parse any overrides from `$ARGUMENTS` and merge with defaults.
## Nx Cloud Connection Check
**CRITICAL**: Before starting the monitoring loop, verify the workspace is connected to Nx Cloud.
### Step 0: Verify Nx Cloud Connection
1. **Check `nx.json`** at workspace root for `nxCloudId` or `nxCloudAccessToken`
2. **If `nx.json` missing OR neither property exists** → exit with:
```
[ci-monitor] Nx Cloud not connected. Unlock 70% faster CI and auto-fix broken PRs with https://nx.dev/nx-cloud
```
3. **If connected** → continue to main loop
## Session Context Behavior
**Important:** Within a Claude Code session, conversation context persists. If you Ctrl+C to interrupt the monitor and re-run `/ci-monitor`, Claude remembers the previous state and may continue from where it left off.
- **To continue monitoring:** Just re-run `/ci-monitor` (context is preserved)
- **To start fresh:** Use `/ci-monitor --fresh` to ignore previous context
- **For a completely clean slate:** Exit Claude Code and restart `claude`
## Default Behaviors by Status
The subagent returns with one of the following statuses. This table defines the **default behavior** for each status. User instructions can override any of these.
| Status | Default Behavior |
| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `ci_success` | Exit with success. Log "CI passed successfully!" |
| `fix_auto_applying` | Fix will be auto-applied by self-healing. Do NOT call MCP. Record `last_cipe_url`, spawn new subagent in wait mode to poll for new CIPE. |
| `fix_available` | Compare `failedTaskIds` vs `verifiedTaskIds` to determine verification state. See **Fix Available Decision Logic** section below. |
| `fix_failed` | Self-healing failed to generate fix. Attempt local fix based on `taskOutputSummary`. If successful → commit, push, loop. If not → exit with failure. |
| `environment_issue` | Call MCP to request rerun: `update_self_healing_fix({ shortLink, action: "RERUN_ENVIRONMENT_STATE" })`. New CIPE spawns automatically. Loop to poll for new CIPE. |
| `no_fix` | CI failed, no fix available (self-healing disabled or not executable). Attempt local fix if possible. Otherwise exit with failure. |
| `no_new_cipe` | Expected CIPE never spawned (CI workflow likely failed before Nx tasks). Report to user, attempt common fixes if configured, or exit with guidance. |
| `polling_timeout` | Subagent polling timeout reached. Exit with timeout. |
| `cipe_canceled` | CIPE was canceled. Exit with canceled status. |
| `cipe_timed_out` | CIPE timed out. Exit with timeout status. |
| `error` | Increment `no_progress_count`. If >= 3 → exit with circuit breaker. Otherwise wait 60s and loop. |
### Fix Available Decision Logic
When subagent returns `fix_available`, main agent compares `failedTaskIds` vs `verifiedTaskIds`:
#### Step 1: Categorize Tasks
1. **Verified tasks** = tasks in both `failedTaskIds` AND `verifiedTaskIds`
2. **Unverified tasks** = tasks in `failedTaskIds` but NOT in `verifiedTaskIds`
3. **E2E tasks** = unverified tasks where target contains "e2e" (task format: `<project>:<target>` or `<project>:<target>:<config>`)
4. **Verifiable tasks** = unverified tasks that are NOT e2e
#### Step 2: Determine Path
| Condition | Path |
| --------------------------------------- | ---------------------------------------- |
| No unverified tasks (all verified) | Apply via MCP |
| Unverified tasks exist, but ALL are e2e | Apply via MCP (treat as verified enough) |
| Verifiable tasks exist | Local verification flow |
#### Step 3a: Apply via MCP (fully/e2e-only verified)
- Call `update_self_healing_fix({ shortLink, action: "APPLY" })`
- Record `last_cipe_url`, spawn subagent in wait mode
#### Step 3b: Local Verification Flow
When verifiable (non-e2e) unverified tasks exist:
1. **Detect package manager:**
- `pnpm-lock.yaml` exists → `pnpm nx`
- `yarn.lock` exists → `yarn nx`
- Otherwise → `npx nx`
2. **Run verifiable tasks in parallel:**
- Spawn `general` subagents to run each task concurrently
- Each subagent runs: `<pm> nx run <taskId>`
- Collect pass/fail results from all subagents
3. **Evaluate results:**
| Result | Action |
| ------------------------- | ---------------------------- |
| ALL verifiable tasks pass | Apply via MCP |
| ANY verifiable task fails | Apply-locally + enhance flow |
4. **Apply-locally + enhance flow:**
- Run `nx apply-locally <shortLink>`
- Enhance the code to fix failing tasks
- Run failing tasks again to verify fix
- If still failing → increment `local_verify_count`, loop back to enhance
- If passing → commit and push, record `expected_commit_sha`, spawn subagent in wait mode
5. **Track attempts** (wraps step 4):
- Increment `local_verify_count` after each enhance cycle
- If `local_verify_count >= local_verify_attempts` (default: 3):
- Get code in commit-able state
- Commit and push with message indicating local verification failed
- Report to user:
```
[ci-monitor] Local verification failed after <N> attempts. Pushed to CI for final validation. Failed: <taskIds>
```
- Record `expected_commit_sha`, spawn subagent in wait mode (let CI be final judge)
#### Commit Message Format
```bash
git commit -m "fix(<projects>): <brief description>
Failed tasks: <taskId1>, <taskId2>
Local verification: passed|enhanced|failed-pushing-to-ci"
```
### Unverified Fix Flow (No Verification Attempted)
When `verificationStatus` is `FAILED`, `NOT_EXECUTABLE`, or fix has `couldAutoApplyTasks != true` with no verification:
- Analyze fix content (`suggestedFix`, `suggestedFixReasoning`, `taskOutputSummary`)
- If fix looks correct → apply via MCP
- If fix needs enhancement → use Apply Locally + Enhance Flow above
- If fix is wrong → reject via MCP, fix from scratch, commit, push
### Auto-Apply Eligibility
The `couldAutoApplyTasks` field indicates whether the fix is eligible for automatic application:
- **`true`**: Fix is eligible for auto-apply. Subagent keeps polling while verification is in progress. Returns `fix_auto_applying` when verified, or `fix_available` if verification fails.
- **`false`** or **`null`**: Fix requires manual action (apply via MCP, apply locally, or reject)
**Key point**: When subagent returns `fix_auto_applying`, do NOT call MCP to apply - self-healing handles it. Just spawn a new subagent in wait mode.
### Apply vs Reject vs Apply Locally
- **Apply via MCP**: Calls `update_self_healing_fix({ shortLink, action: "APPLY" })`. Self-healing agent applies the fix in CI and a new CIPE spawns automatically. No local git operations needed.
- **Apply Locally**: Runs `nx apply-locally <shortLink>`. Applies the patch to your local working directory and sets state to `APPLIED_LOCALLY`. Use this when you want to enhance the fix before pushing.
- **Reject via MCP**: Calls `update_self_healing_fix({ shortLink, action: "REJECT" })`. Marks fix as rejected. Use only when the fix is completely wrong and you'll fix from scratch.
### Apply Locally + Enhance Flow
When the fix needs enhancement (use `nx apply-locally`, NOT reject):
1. Apply the patch locally: `nx apply-locally <shortLink>` (this also updates state to `APPLIED_LOCALLY`)
2. Make additional changes as needed
3. Commit and push:
```bash
git add -A
git commit -m "fix: resolve <failedTaskIds>"
git push origin $(git branch --show-current)
```
4. Loop to poll for new CIPE
### Reject + Fix From Scratch Flow
When the fix is completely wrong:
1. Call MCP to reject: `update_self_healing_fix({ shortLink, action: "REJECT" })`
2. Fix the issue from scratch locally
3. Commit and push:
```bash
git add -A
git commit -m "fix: resolve <failedTaskIds>"
git push origin $(git branch --show-current)
```
4. Loop to poll for new CIPE
### Environment Issue Handling
When `failureClassification == 'ENVIRONMENT_STATE'`:
1. Call MCP to request rerun: `update_self_healing_fix({ shortLink, action: "RERUN_ENVIRONMENT_STATE" })`
2. New CIPE spawns automatically (no local git operations needed)
3. Loop to poll for new CIPE with `previousCipeUrl` set
### No-New-CIPE Handling
When `status == 'no_new_cipe'`:
This means the expected CIPE was never created - CI likely failed before Nx tasks could run.
1. **Report to user:**
```
[ci-monitor] No CI attempt for <sha> after 10 min. Check CI provider for pre-Nx failures (install, checkout, auth). Last CI attempt: <previousCipeUrl>
```
2. **If user configured auto-fix attempts** (e.g., `--auto-fix-workflow`):
- Detect package manager: check for `pnpm-lock.yaml`, `yarn.lock`, `package-lock.json`
- Run install to update lockfile:
```bash
pnpm install # or npm install / yarn install
```
- If lockfile changed:
```bash
git add pnpm-lock.yaml # or appropriate lockfile
git commit -m "chore: update lockfile"
git push origin $(git branch --show-current)
```
- Record new commit SHA, loop to poll with `expectedCommitSha`
3. **Otherwise:** Exit with `no_new_cipe` status, providing guidance for user to investigate
## Exit Conditions
Exit the monitoring loop when ANY of these conditions are met:
| Condition | Exit Type |
| ------------------------------------------- | ---------------- |
| CI passes (`cipeStatus == 'SUCCEEDED'`) | Success |
| Max CIPE cycles reached | Timeout |
| Max duration reached | Timeout |
| 3 consecutive no-progress iterations | Circuit breaker |
| No fix available and local fix not possible | Failure |
| No new CIPE and auto-fix not configured | Pre-CIPE failure |
| User cancels | Cancelled |
## Main Loop
### Step 1: Initialize Tracking
```
cycle_count = 0
start_time = now()
no_progress_count = 0
local_verify_count = 0
last_state = null
last_cipe_url = null
expected_commit_sha = null
```
### Step 2: Spawn Subagent
Spawn the `ci-watcher` subagent to poll CI status:
**Fresh start (first spawn, no expected CIPE):**
```
Task(
agent: "ci-watcher",
prompt: "Monitor CI for branch '<branch>'.
Subagent timeout: <subagent-timeout> minutes.
New-CIPE timeout: <new-cipe-timeout> minutes.
Verbosity: <verbosity>."
)
```
**After action that triggers new CIPE (wait mode):**
```
Task(
agent: "ci-watcher",
prompt: "Monitor CI for branch '<branch>'.
Subagent timeout: <subagent-timeout> minutes.
New-CIPE timeout: <new-cipe-timeout> minutes.
Verbosity: <verbosity>.
WAIT MODE: A new CIPE should spawn. Ignore old CIPE until new one appears.
Expected commit SHA: <expected_commit_sha>
Previous CIPE URL: <last_cipe_url>"
)
```
### Step 3: Handle Subagent Response
When subagent returns:
1. Check the returned status
2. Look up default behavior in the table above
3. Check if user instructions override the default
4. Execute the appropriate action
5. **If action expects new CIPE**, update tracking (see Step 3a)
6. If action results in looping, go to Step 2
### Step 3a: Track State for New-CIPE Detection
After actions that should trigger a new CIPE, record state before looping:
| Action | What to Track | Subagent Mode |
| ----------------------------- | --------------------------------------------- | ------------- |
| Fix auto-applying | `last_cipe_url = current cipeUrl` | Wait mode |
| Apply via MCP | `last_cipe_url = current cipeUrl` | Wait mode |
| Apply locally + push | `expected_commit_sha = $(git rev-parse HEAD)` | Wait mode |
| Reject + fix + push | `expected_commit_sha = $(git rev-parse HEAD)` | Wait mode |
| Fix failed + local fix + push | `expected_commit_sha = $(git rev-parse HEAD)` | Wait mode |
| No fix + local fix + push | `expected_commit_sha = $(git rev-parse HEAD)` | Wait mode |
| Environment rerun | `last_cipe_url = current cipeUrl` | Wait mode |
| No-new-CIPE + auto-fix + push | `expected_commit_sha = $(git rev-parse HEAD)` | Wait mode |
**CRITICAL**: When passing `expectedCommitSha` or `last_cipe_url` to the subagent, it enters **wait mode**:
- Subagent will **completely ignore** the old/stale CIPE
- Subagent will only wait for new CIPE to appear
- Subagent will NOT return to main agent with stale CIPE data
- Once new CIPE detected, subagent switches to normal polling
**Why wait mode matters for context preservation**: Stale CIPE data can be very large (task output summaries, suggested fix patches, reasoning). If subagent returns this to main agent, it pollutes main agent's context with useless data since we already processed that CIPE. Wait mode keeps stale data in the subagent, never sending it to main agent.
### Step 4: Progress Tracking
After each action:
- If state changed significantly → reset `no_progress_count = 0`
- If state unchanged → `no_progress_count++`
- On new CI attempt detected → reset `local_verify_count = 0`
## Status Reporting
Based on verbosity level:
| Level | What to Report |
| --------- | -------------------------------------------------------------------------- |
| `minimal` | Only final result (success/failure/timeout) |
| `medium` | State changes + periodic updates ("Cycle N \| Elapsed: Xm \| Status: ...") |
| `verbose` | All of medium + full subagent responses, git outputs, MCP responses |
## User Instruction Examples
Users can override default behaviors:
| Instruction | Effect |
| ------------------------------------------------ | --------------------------------------------- |
| "never auto-apply" | Always prompt before applying any fix |
| "always ask before git push" | Prompt before each push |
| "reject any fix for e2e tasks" | Auto-reject if `failedTaskIds` contains e2e |
| "apply all fixes regardless of verification" | Skip verification check, apply everything |
| "if confidence < 70, reject" | Check confidence field before applying |
| "run 'nx affected -t typecheck' before applying" | Add local verification step |
| "auto-fix workflow failures" | Attempt lockfile updates on pre-CIPE failures |
| "wait 45 min for new CIPE" | Override new-CIPE timeout (default: 10 min) |
## Error Handling
| Error | Action |
| ------------------------ | ------------------------------------------------------------------------------------- |
| Git rebase conflict | Report to user, exit |
| `nx apply-locally` fails | Report to user, attempt manual patch or exit |
| MCP tool error | Retry once, if fails report to user |
| Subagent spawn failure | Retry once, if fails exit with error |
| No new CIPE detected | If `--auto-fix-workflow`, try lockfile update; otherwise report to user with guidance |
| Lockfile auto-fix fails | Report to user, exit with guidance to check CI logs |
## Example Session
### Example 1: Normal Flow with Self-Healing (medium verbosity)
```
[ci-monitor] Starting CI monitor for branch 'feature/add-auth'
[ci-monitor] Config: max-cycles=5, timeout=120m, verbosity=medium
[ci-monitor] Spawning subagent to poll CI status...
[CI Monitor] CI attempt: IN_PROGRESS | Self-Healing: NOT_STARTED | Elapsed: 1m
[CI Monitor] CI attempt: FAILED | Self-Healing: IN_PROGRESS | Elapsed: 3m
[CI Monitor] CI attempt: FAILED | Self-Healing: COMPLETED | Elapsed: 5m
[ci-monitor] Fix available! Verification: COMPLETED
[ci-monitor] Applying fix via MCP...
[ci-monitor] Fix applied in CI. Waiting for new CI attempt...
[ci-monitor] Spawning subagent to poll CI status...
[CI Monitor] New CI attempt detected!
[CI Monitor] CI attempt: SUCCEEDED | Elapsed: 8m
[ci-monitor] CI passed successfully!
[ci-monitor] Summary:
- Total cycles: 2
- Total time: 12m 34s
- Fixes applied: 1
- Result: SUCCESS
```
### Example 2: Pre-CI Failure (medium verbosity)
```
[ci-monitor] Starting CI monitor for branch 'feature/add-products'
[ci-monitor] Config: max-cycles=5, timeout=120m, auto-fix-workflow=true
[ci-monitor] Spawning subagent to poll CI status...
[CI Monitor] CI attempt: FAILED | Self-Healing: COMPLETED | Elapsed: 2m
[ci-monitor] Applying fix locally, enhancing, and pushing...
[ci-monitor] Committed: abc1234
[ci-monitor] Spawning subagent to poll CI status...
[CI Monitor] Waiting for new CI attempt... (expected SHA: abc1234)
[CI Monitor] ⚠️ CI attempt timeout (10 min). Returning no_new_cipe.
[ci-monitor] Status: no_new_cipe
[ci-monitor] --auto-fix-workflow enabled. Attempting lockfile update...
[ci-monitor] Lockfile updated. Committed: def5678
[ci-monitor] Spawning subagent to poll CI status...
[CI Monitor] New CI attempt detected!
[CI Monitor] CI attempt: SUCCEEDED | Elapsed: 18m
[ci-monitor] CI passed successfully!
[ci-monitor] Summary:
- Total cycles: 3
- Total time: 22m 15s
- Fixes applied: 1 (self-healing) + 1 (lockfile)
- Result: SUCCESS
```
+437
View File
@@ -0,0 +1,437 @@
---
name: ci-monitor
description: Monitor Nx Cloud CI pipeline and handle self-healing fixes automatically. Checks for Nx Cloud connection before starting.
---
# CI Monitor Command
You are the orchestrator for monitoring Nx Cloud CI pipeline executions and handling self-healing fixes. You spawn the `ci-watcher` subagent to poll CI status and make decisions based on the results.
## Context
- **Current Branch:** !`git branch --show-current`
- **Current Commit:** !`git rev-parse --short HEAD`
- **Remote Status:** !`git status -sb | head -1`
## User Instructions
$ARGUMENTS
**Important:** If user provides specific instructions, respect them over default behaviors described below.
## Configuration Defaults
| Setting | Default | Description |
| ------------------------- | ------------- | ------------------------------------------------------------------- |
| `--max-cycles` | 10 | Maximum CIPE cycles before timeout |
| `--timeout` | 120 | Maximum duration in minutes |
| `--verbosity` | medium | Output level: minimal, medium, verbose |
| `--branch` | (auto-detect) | Branch to monitor |
| `--subagent-timeout` | 60 | Subagent polling timeout in minutes |
| `--fresh` | false | Ignore previous context, start fresh |
| `--auto-fix-workflow` | false | Attempt common fixes for pre-CIPE failures (e.g., lockfile updates) |
| `--new-cipe-timeout` | 10 | Minutes to wait for new CIPE after action |
| `--local-verify-attempts` | 3 | Max local verification + enhance cycles before pushing to CI |
Parse any overrides from `$ARGUMENTS` and merge with defaults.
## Nx Cloud Connection Check
**CRITICAL**: Before starting the monitoring loop, verify the workspace is connected to Nx Cloud.
### Step 0: Verify Nx Cloud Connection
1. **Check `nx.json`** at workspace root for `nxCloudId` or `nxCloudAccessToken`
2. **If `nx.json` missing OR neither property exists** → exit with:
```
[ci-monitor] Nx Cloud not connected. Unlock 70% faster CI and auto-fix broken PRs with https://nx.dev/nx-cloud
```
3. **If connected** → continue to main loop
## Session Context Behavior
**Important:** Within a Claude Code session, conversation context persists. If you Ctrl+C to interrupt the monitor and re-run `/ci-monitor`, Claude remembers the previous state and may continue from where it left off.
- **To continue monitoring:** Just re-run `/ci-monitor` (context is preserved)
- **To start fresh:** Use `/ci-monitor --fresh` to ignore previous context
- **For a completely clean slate:** Exit Claude Code and restart `claude`
## Default Behaviors by Status
The subagent returns with one of the following statuses. This table defines the **default behavior** for each status. User instructions can override any of these.
| Status | Default Behavior |
| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `ci_success` | Exit with success. Log "CI passed successfully!" |
| `fix_auto_applying` | Fix will be auto-applied by self-healing. Do NOT call MCP. Record `last_cipe_url`, spawn new subagent in wait mode to poll for new CIPE. |
| `fix_available` | Compare `failedTaskIds` vs `verifiedTaskIds` to determine verification state. See **Fix Available Decision Logic** section below. |
| `fix_failed` | Self-healing failed to generate fix. Attempt local fix based on `taskOutputSummary`. If successful → commit, push, loop. If not → exit with failure. |
| `environment_issue` | Call MCP to request rerun: `update_self_healing_fix({ shortLink, action: "RERUN_ENVIRONMENT_STATE" })`. New CIPE spawns automatically. Loop to poll for new CIPE. |
| `no_fix` | CI failed, no fix available (self-healing disabled or not executable). Attempt local fix if possible. Otherwise exit with failure. |
| `no_new_cipe` | Expected CIPE never spawned (CI workflow likely failed before Nx tasks). Report to user, attempt common fixes if configured, or exit with guidance. |
| `polling_timeout` | Subagent polling timeout reached. Exit with timeout. |
| `cipe_canceled` | CIPE was canceled. Exit with canceled status. |
| `cipe_timed_out` | CIPE timed out. Exit with timeout status. |
| `error` | Increment `no_progress_count`. If >= 3 → exit with circuit breaker. Otherwise wait 60s and loop. |
### Fix Available Decision Logic
When subagent returns `fix_available`, main agent compares `failedTaskIds` vs `verifiedTaskIds`:
#### Step 1: Categorize Tasks
1. **Verified tasks** = tasks in both `failedTaskIds` AND `verifiedTaskIds`
2. **Unverified tasks** = tasks in `failedTaskIds` but NOT in `verifiedTaskIds`
3. **E2E tasks** = unverified tasks where target contains "e2e" (task format: `<project>:<target>` or `<project>:<target>:<config>`)
4. **Verifiable tasks** = unverified tasks that are NOT e2e
#### Step 2: Determine Path
| Condition | Path |
| --------------------------------------- | ---------------------------------------- |
| No unverified tasks (all verified) | Apply via MCP |
| Unverified tasks exist, but ALL are e2e | Apply via MCP (treat as verified enough) |
| Verifiable tasks exist | Local verification flow |
#### Step 3a: Apply via MCP (fully/e2e-only verified)
- Call `update_self_healing_fix({ shortLink, action: "APPLY" })`
- Record `last_cipe_url`, spawn subagent in wait mode
#### Step 3b: Local Verification Flow
When verifiable (non-e2e) unverified tasks exist:
1. **Detect package manager:**
- `pnpm-lock.yaml` exists → `pnpm nx`
- `yarn.lock` exists → `yarn nx`
- Otherwise → `npx nx`
2. **Run verifiable tasks in parallel:**
- Spawn `general` subagents to run each task concurrently
- Each subagent runs: `<pm> nx run <taskId>`
- Collect pass/fail results from all subagents
3. **Evaluate results:**
| Result | Action |
| ------------------------- | ---------------------------- |
| ALL verifiable tasks pass | Apply via MCP |
| ANY verifiable task fails | Apply-locally + enhance flow |
4. **Apply-locally + enhance flow:**
- Run `nx apply-locally <shortLink>`
- Enhance the code to fix failing tasks
- Run failing tasks again to verify fix
- If still failing → increment `local_verify_count`, loop back to enhance
- If passing → commit and push, record `expected_commit_sha`, spawn subagent in wait mode
5. **Track attempts** (wraps step 4):
- Increment `local_verify_count` after each enhance cycle
- If `local_verify_count >= local_verify_attempts` (default: 3):
- Get code in commit-able state
- Commit and push with message indicating local verification failed
- Report to user:
```
[ci-monitor] Local verification failed after <N> attempts. Pushed to CI for final validation. Failed: <taskIds>
```
- Record `expected_commit_sha`, spawn subagent in wait mode (let CI be final judge)
#### Commit Message Format
```bash
git commit -m "fix(<projects>): <brief description>
Failed tasks: <taskId1>, <taskId2>
Local verification: passed|enhanced|failed-pushing-to-ci"
```
### Unverified Fix Flow (No Verification Attempted)
When `verificationStatus` is `FAILED`, `NOT_EXECUTABLE`, or fix has `couldAutoApplyTasks != true` with no verification:
- Analyze fix content (`suggestedFix`, `suggestedFixReasoning`, `taskOutputSummary`)
- If fix looks correct → apply via MCP
- If fix needs enhancement → use Apply Locally + Enhance Flow above
- If fix is wrong → reject via MCP, fix from scratch, commit, push
### Auto-Apply Eligibility
The `couldAutoApplyTasks` field indicates whether the fix is eligible for automatic application:
- **`true`**: Fix is eligible for auto-apply. Subagent keeps polling while verification is in progress. Returns `fix_auto_applying` when verified, or `fix_available` if verification fails.
- **`false`** or **`null`**: Fix requires manual action (apply via MCP, apply locally, or reject)
**Key point**: When subagent returns `fix_auto_applying`, do NOT call MCP to apply - self-healing handles it. Just spawn a new subagent in wait mode.
### Apply vs Reject vs Apply Locally
- **Apply via MCP**: Calls `update_self_healing_fix({ shortLink, action: "APPLY" })`. Self-healing agent applies the fix in CI and a new CIPE spawns automatically. No local git operations needed.
- **Apply Locally**: Runs `nx apply-locally <shortLink>`. Applies the patch to your local working directory and sets state to `APPLIED_LOCALLY`. Use this when you want to enhance the fix before pushing.
- **Reject via MCP**: Calls `update_self_healing_fix({ shortLink, action: "REJECT" })`. Marks fix as rejected. Use only when the fix is completely wrong and you'll fix from scratch.
### Apply Locally + Enhance Flow
When the fix needs enhancement (use `nx apply-locally`, NOT reject):
1. Apply the patch locally: `nx apply-locally <shortLink>` (this also updates state to `APPLIED_LOCALLY`)
2. Make additional changes as needed
3. Commit and push:
```bash
git add -A
git commit -m "fix: resolve <failedTaskIds>"
git push origin $(git branch --show-current)
```
4. Loop to poll for new CIPE
### Reject + Fix From Scratch Flow
When the fix is completely wrong:
1. Call MCP to reject: `update_self_healing_fix({ shortLink, action: "REJECT" })`
2. Fix the issue from scratch locally
3. Commit and push:
```bash
git add -A
git commit -m "fix: resolve <failedTaskIds>"
git push origin $(git branch --show-current)
```
4. Loop to poll for new CIPE
### Environment Issue Handling
When `failureClassification == 'ENVIRONMENT_STATE'`:
1. Call MCP to request rerun: `update_self_healing_fix({ shortLink, action: "RERUN_ENVIRONMENT_STATE" })`
2. New CIPE spawns automatically (no local git operations needed)
3. Loop to poll for new CIPE with `previousCipeUrl` set
### No-New-CIPE Handling
When `status == 'no_new_cipe'`:
This means the expected CIPE was never created - CI likely failed before Nx tasks could run.
1. **Report to user:**
```
[ci-monitor] No CI attempt for <sha> after 10 min. Check CI provider for pre-Nx failures (install, checkout, auth). Last CI attempt: <previousCipeUrl>
```
2. **If user configured auto-fix attempts** (e.g., `--auto-fix-workflow`):
- Detect package manager: check for `pnpm-lock.yaml`, `yarn.lock`, `package-lock.json`
- Run install to update lockfile:
```bash
pnpm install # or npm install / yarn install
```
- If lockfile changed:
```bash
git add pnpm-lock.yaml # or appropriate lockfile
git commit -m "chore: update lockfile"
git push origin $(git branch --show-current)
```
- Record new commit SHA, loop to poll with `expectedCommitSha`
3. **Otherwise:** Exit with `no_new_cipe` status, providing guidance for user to investigate
## Exit Conditions
Exit the monitoring loop when ANY of these conditions are met:
| Condition | Exit Type |
| ------------------------------------------- | ---------------- |
| CI passes (`cipeStatus == 'SUCCEEDED'`) | Success |
| Max CIPE cycles reached | Timeout |
| Max duration reached | Timeout |
| 3 consecutive no-progress iterations | Circuit breaker |
| No fix available and local fix not possible | Failure |
| No new CIPE and auto-fix not configured | Pre-CIPE failure |
| User cancels | Cancelled |
## Main Loop
### Step 1: Initialize Tracking
```
cycle_count = 0
start_time = now()
no_progress_count = 0
local_verify_count = 0
last_state = null
last_cipe_url = null
expected_commit_sha = null
```
### Step 2: Spawn Subagent
Spawn the `ci-watcher` subagent to poll CI status:
**Fresh start (first spawn, no expected CIPE):**
```
Task(
agent: "ci-watcher",
prompt: "Monitor CI for branch '<branch>'.
Subagent timeout: <subagent-timeout> minutes.
New-CIPE timeout: <new-cipe-timeout> minutes.
Verbosity: <verbosity>."
)
```
**After action that triggers new CIPE (wait mode):**
```
Task(
agent: "ci-watcher",
prompt: "Monitor CI for branch '<branch>'.
Subagent timeout: <subagent-timeout> minutes.
New-CIPE timeout: <new-cipe-timeout> minutes.
Verbosity: <verbosity>.
WAIT MODE: A new CIPE should spawn. Ignore old CIPE until new one appears.
Expected commit SHA: <expected_commit_sha>
Previous CIPE URL: <last_cipe_url>"
)
```
### Step 3: Handle Subagent Response
When subagent returns:
1. Check the returned status
2. Look up default behavior in the table above
3. Check if user instructions override the default
4. Execute the appropriate action
5. **If action expects new CIPE**, update tracking (see Step 3a)
6. If action results in looping, go to Step 2
### Step 3a: Track State for New-CIPE Detection
After actions that should trigger a new CIPE, record state before looping:
| Action | What to Track | Subagent Mode |
| ----------------------------- | --------------------------------------------- | ------------- |
| Fix auto-applying | `last_cipe_url = current cipeUrl` | Wait mode |
| Apply via MCP | `last_cipe_url = current cipeUrl` | Wait mode |
| Apply locally + push | `expected_commit_sha = $(git rev-parse HEAD)` | Wait mode |
| Reject + fix + push | `expected_commit_sha = $(git rev-parse HEAD)` | Wait mode |
| Fix failed + local fix + push | `expected_commit_sha = $(git rev-parse HEAD)` | Wait mode |
| No fix + local fix + push | `expected_commit_sha = $(git rev-parse HEAD)` | Wait mode |
| Environment rerun | `last_cipe_url = current cipeUrl` | Wait mode |
| No-new-CIPE + auto-fix + push | `expected_commit_sha = $(git rev-parse HEAD)` | Wait mode |
**CRITICAL**: When passing `expectedCommitSha` or `last_cipe_url` to the subagent, it enters **wait mode**:
- Subagent will **completely ignore** the old/stale CIPE
- Subagent will only wait for new CIPE to appear
- Subagent will NOT return to main agent with stale CIPE data
- Once new CIPE detected, subagent switches to normal polling
**Why wait mode matters for context preservation**: Stale CIPE data can be very large (task output summaries, suggested fix patches, reasoning). If subagent returns this to main agent, it pollutes main agent's context with useless data since we already processed that CIPE. Wait mode keeps stale data in the subagent, never sending it to main agent.
### Step 4: Progress Tracking
After each action:
- If state changed significantly → reset `no_progress_count = 0`
- If state unchanged → `no_progress_count++`
- On new CI attempt detected → reset `local_verify_count = 0`
## Status Reporting
Based on verbosity level:
| Level | What to Report |
| --------- | -------------------------------------------------------------------------- |
| `minimal` | Only final result (success/failure/timeout) |
| `medium` | State changes + periodic updates ("Cycle N \| Elapsed: Xm \| Status: ...") |
| `verbose` | All of medium + full subagent responses, git outputs, MCP responses |
## User Instruction Examples
Users can override default behaviors:
| Instruction | Effect |
| ------------------------------------------------ | --------------------------------------------- |
| "never auto-apply" | Always prompt before applying any fix |
| "always ask before git push" | Prompt before each push |
| "reject any fix for e2e tasks" | Auto-reject if `failedTaskIds` contains e2e |
| "apply all fixes regardless of verification" | Skip verification check, apply everything |
| "if confidence < 70, reject" | Check confidence field before applying |
| "run 'nx affected -t typecheck' before applying" | Add local verification step |
| "auto-fix workflow failures" | Attempt lockfile updates on pre-CIPE failures |
| "wait 45 min for new CIPE" | Override new-CIPE timeout (default: 10 min) |
## Error Handling
| Error | Action |
| ------------------------ | ------------------------------------------------------------------------------------- |
| Git rebase conflict | Report to user, exit |
| `nx apply-locally` fails | Report to user, attempt manual patch or exit |
| MCP tool error | Retry once, if fails report to user |
| Subagent spawn failure | Retry once, if fails exit with error |
| No new CIPE detected | If `--auto-fix-workflow`, try lockfile update; otherwise report to user with guidance |
| Lockfile auto-fix fails | Report to user, exit with guidance to check CI logs |
## Example Session
### Example 1: Normal Flow with Self-Healing (medium verbosity)
```
[ci-monitor] Starting CI monitor for branch 'feature/add-auth'
[ci-monitor] Config: max-cycles=5, timeout=120m, verbosity=medium
[ci-monitor] Spawning subagent to poll CI status...
[CI Monitor] CI attempt: IN_PROGRESS | Self-Healing: NOT_STARTED | Elapsed: 1m
[CI Monitor] CI attempt: FAILED | Self-Healing: IN_PROGRESS | Elapsed: 3m
[CI Monitor] CI attempt: FAILED | Self-Healing: COMPLETED | Elapsed: 5m
[ci-monitor] Fix available! Verification: COMPLETED
[ci-monitor] Applying fix via MCP...
[ci-monitor] Fix applied in CI. Waiting for new CI attempt...
[ci-monitor] Spawning subagent to poll CI status...
[CI Monitor] New CI attempt detected!
[CI Monitor] CI attempt: SUCCEEDED | Elapsed: 8m
[ci-monitor] CI passed successfully!
[ci-monitor] Summary:
- Total cycles: 2
- Total time: 12m 34s
- Fixes applied: 1
- Result: SUCCESS
```
### Example 2: Pre-CI Failure (medium verbosity)
```
[ci-monitor] Starting CI monitor for branch 'feature/add-products'
[ci-monitor] Config: max-cycles=5, timeout=120m, auto-fix-workflow=true
[ci-monitor] Spawning subagent to poll CI status...
[CI Monitor] CI attempt: FAILED | Self-Healing: COMPLETED | Elapsed: 2m
[ci-monitor] Applying fix locally, enhancing, and pushing...
[ci-monitor] Committed: abc1234
[ci-monitor] Spawning subagent to poll CI status...
[CI Monitor] Waiting for new CI attempt... (expected SHA: abc1234)
[CI Monitor] ⚠️ CI attempt timeout (10 min). Returning no_new_cipe.
[ci-monitor] Status: no_new_cipe
[ci-monitor] --auto-fix-workflow enabled. Attempting lockfile update...
[ci-monitor] Lockfile updated. Committed: def5678
[ci-monitor] Spawning subagent to poll CI status...
[CI Monitor] New CI attempt detected!
[CI Monitor] CI attempt: SUCCEEDED | Elapsed: 18m
[ci-monitor] CI passed successfully!
[ci-monitor] Summary:
- Total cycles: 3
- Total time: 22m 15s
- Fixes applied: 1 (self-healing) + 1 (lockfile)
- Result: SUCCESS
```
+228
View File
@@ -0,0 +1,228 @@
---
name: nx-generate
description: Generate code using nx generators. USE WHEN scaffolding code or transforming existing code - for example creating libraries or applications, or anything else that is boilerplate code or automates repetitive tasks. ALWAYS use this first when generating code with Nx instead of calling MCP tools or running nx generate immediately.
---
# Run Nx Generator
Nx generators are powerful tools that scaffold projects, make automated code migrations or automate repetitive tasks in a monorepo. They ensure consistency across the codebase and reduce boilerplate work.
This skill applies when the user wants to:
- Create new projects like libraries or applications
- Scaffold features or boilerplate code
- Run workspace-specific or custom generators
- Do anything else that an nx generator exists for
## Generator Discovery Flow
### Step 1: List Available Generators
Use the Nx CLI to discover available generators:
- List all generators for a plugin: `npx nx list @nx/react`
- View available plugins: `npx nx list`
This includes:
- Plugin generators (e.g., `@nx/react:library`, `@nx/js:library`)
- Local workspace generators (defined in the repo's own plugins)
### Step 2: Match Generator to User Request
Based on the user's request, identify which generator(s) could fulfill their needs. Consider:
- What artifact type they want to create (library, application, etc.)
- Which framework or technology stack is relevant
- Whether they mentioned specific generator names
**IMPORTANT**: When both a local workspace generator and an external plugin generator could satisfy the request, **always prefer the local workspace generator**. Local generators are customized for the specific repo's patterns and conventions.
It's possible that the user request is something that no Nx generator exists for whatsoever. In this case, you can stop using this skill and try to help the user another way. HOWEVER, the burden of proof for this is high. Before aborting, carefully consider each and every generator that's available. Look into details for any that could be related in any way before making this decision.
## Pre-Execution Checklist
Before running any generator, complete these steps:
### 1. Fetch Generator Schema
Use the `--help` flag to understand all available options:
```bash
npx nx g @nx/react:library --help
```
Pay attention to:
- Required options that must be provided
- Optional options that may be relevant to the user's request
- Default values that might need to be overridden
### 2. Read Generator Source Code
Understanding what the generator actually does helps you:
- Know what files will be created/modified
- Understand any side effects (updating configs, installing deps, etc.)
- Identify options that might not be obvious from the schema
To find generator source code:
- For plugin generators: Use `node -e "console.log(require.resolve('@nx/<plugin>/generators.json'));"` to find the generators.json, then locate the source from there
- If that fails, read directly from `node_modules/<plugin>/generators.json`
- For local generators: They are typically in `tools/generators/` or a local plugin directory. You can search the repo for the generator name to find it.
### 2.5 Reevaluate if the generator is right
Once you have built up an understanding of what the selected generator does, reconsider: Is this the right generator to service the user request?
If not, it's okay to go back to the Generator Discovery Flow and select a different generator before proceeding. If you do, make sure to go through the entire pre-execution checklist once more.
### 3. Understand Repo Context
Before generating, examine the target area of the codebase:
- Look at similar existing artifacts (other libraries, applications, etc.)
- Identify patterns and conventions used in the repo
- Note naming conventions, file structures, and configuration patterns
- Try to match these patterns when configuring the generator
For example, if similar libraries are using a specific test runner, build tool or linter, try to match that if possible.
If projects or other artifacts are organized with a specific naming convention, try to match it.
### 4. Validate Required Options
Ensure all required options have values:
- Map the user's request to generator options
- Infer values from context where possible
- Ask the user for any critical missing information
## Execution
Keep in mind that you might have to prefix things with npx/pnpx/yarn if the user doesn't have nx installed globally.
Many generators will behave differently based on where they are executed. For example, first-party nx library generators use the cwd to determine the directory that the library should be placed in. This is highly important.
### Consider Dry-Run (Optional)
Running with `--dry-run` first is strongly encouraged but not mandatory. Use your judgment:
- For complex generators or unfamiliar territory: do a dry-run first
- For simple, well-understood generators: may proceed directly
- Dry-run shows file names and created/deleted/modified markers, but not content
- There are cases where a generator does not support dry-run (for example if it had to install an npm package) - in that case --dry-run might fail. Don't be discouraged but simply move on to running the generator for real and iterating from there.
### Running the Generator
Execute the generator with:
```bash
nx generate <generator-name> <options> --no-interactive
```
**CRITICAL**: Always include `--no-interactive` to prevent prompts that would hang the execution.
Example:
```bash
nx generate @nx/react:library --name=my-utils --no-interactive
```
### Handling Generator Failures
If the generator fails:
1. **Diagnose the error** - Read the error message carefully
2. **Identify the cause** - Missing options, invalid values, conflicts, etc.
3. **Attempt automatic fix** - Adjust options or resolve conflicts
4. **Retry** - Run the generator again with corrected options
Common failure reasons:
- Missing required options
- Invalid option values
- Conflicting with existing files
- Missing dependencies
- Generator doesn't support certain flag combinations
## Post-Generation
### 1. Modify Generated Code (If Needed)
Generators provide a starting point, but the output may need adjustment to match the user's specific requirements:
- Add or modify functionality as requested
- Adjust imports, exports, or configurations
- Integrate with existing code patterns in the repo
### 2. Format Code
Run formatting on all generated/modified files:
```bash
nx format --fix
```
Languages other than javascript/typescript might need other formatting invocations too.
### 3. Run Verification
Verify that the generated code works correctly. What this looks like will vary depending on the type of generator and the targets available.
If the generator created a new project, run its targets directly
Use your best judgement to determine what needs to be verified.
Example:
```bash
nx lint <new-project>
nx test <new-project>
nx build <new-project>
```
### 4. Handle Verification Failures
When verification fails:
**If scope is manageable** (a few lint errors, minor type issues):
- Fix the issues
- Re-run verification to confirm
**If issues are extensive** (many errors, complex problems):
- Attempt simple, obvious fixes first
- If still failing, escalate to the user with:
- Description of what was generated
- What verification is failing
- What you've attempted to fix
- Remaining issues that need user input
## Error Handling
### Generator Failures
- Check the error message for specific causes
- Verify all required options are provided
- Check for conflicts with existing files
- Ensure the generator name and options are correct
### Missing Options
- Consult the generator schema for required fields
- Infer values from context when reasonable
- Ask the user for values that cannot be inferred
## Key Principles
1. **Local generators first** - Always prefer workspace/local generators over external plugin generators when both could work
2. **Understand before running** - Read both the schema AND the source code to fully understand what will happen
3. **No prompts** - Always use `--no-interactive` to prevent hanging
4. **Generators are starting points** - Modify the output as needed to fully satisfy the user's requirements
5. **Verify changes work** - Don't just generate; ensure the code builds, lints, and tests pass
6. **Be proactive about fixes** - Don't just report errors; attempt to resolve them automatically when possible
7. **Match repo patterns** - Study existing similar code in the repo and match its conventions
+9
View File
@@ -0,0 +1,9 @@
---
name: nx-plugins
description: Find and add Nx plugins. USE WHEN user wants to discover available plugins, install a new plugin, or add support for a specific framework or technology to the workspace.
---
## Finding and Installing new plugins
- List plugins: `pnpm nx list`
- Install plugins `pnpm nx add <plugin>`. Example: `pnpm nx add @nx/react`.
+58
View File
@@ -0,0 +1,58 @@
---
name: nx-run-tasks
description: Helps with running tasks in an Nx workspace. USE WHEN the user wants to execute build, test, lint, serve, or run any other tasks defined in the workspace.
---
You can run tasks with Nx in the following way.
Keep in mind that you might have to prefix things with npx/pnpx/yarn if the user doesn't have nx installed globally. Look at the package.json or lockfile to determine which package manager is in use.
For more details on any command, run it with `--help` (e.g. `nx run-many --help`, `nx affected --help`).
## Understand which tasks can be run
You can check those via `nx show project <projectname> --json`, for example `nx show project myapp --json`. It contains a `targets` section which has information about targets that can be run. You can also just look at the `package.json` scripts or `project.json` targets, but you might miss out on inferred tasks by Nx plugins.
## Run a single task
```
nx run <project>:<task>
```
where `project` is the project name defined in `package.json` or `project.json` (if present).
## Run multiple tasks
```
nx run-many -t build test lint typecheck
```
You can pass a `-p` flag to filter to specific projects, otherwise it runs on all projects. You can also use `--exclude` to exclude projects, and `--parallel` to control the number of parallel processes (default is 3).
Examples:
- `nx run-many -t test -p proj1 proj2` — test specific projects
- `nx run-many -t test --projects=*-app --exclude=excluded-app` — test projects matching a pattern
- `nx run-many -t test --projects=tag:api-*` — test projects by tag
## Run tasks for affected projects
Use `nx affected` to only run tasks on projects that have been changed and projects that depend on changed projects. This is especially useful in CI and for large workspaces.
```
nx affected -t build test lint
```
By default it compares against the base branch. You can customize this:
- `nx affected -t test --base=main --head=HEAD` — compare against a specific base and head
- `nx affected -t test --files=libs/mylib/src/index.ts` — specify changed files directly
## Useful flags
These flags work with `run`, `run-many`, and `affected`:
- `--skipNxCache` — rerun tasks even when results are cached
- `--verbose` — print additional information such as stack traces
- `--nxBail` — stop execution after the first failed task
- `--configuration=<name>` — use a specific configuration (e.g. `production`)
+186
View File
@@ -0,0 +1,186 @@
---
name: nx-workspace
description: "Explore and understand Nx workspaces. USE WHEN answering any questions about the nx workspace, the projects in it or tasks to run. EXAMPLES: 'What projects are in this workspace?', 'How is project X configured?', 'What targets can I run?', 'What's affected by my changes?', 'Which projects depend on library Y?', or any questions about Nx workspace structure, project configuration, or available tasks."
---
# Nx Workspace Exploration
This skill provides read-only exploration of Nx workspaces. Use it to understand workspace structure, project configuration, available targets, and dependencies.
Keep in mind that you might have to prefix commands with `npx`/`pnpx`/`yarn` if nx isn't installed globally. Check the lockfile to determine the package manager in use.
## Listing Projects
Use `nx show projects` to list projects in the workspace.
```bash
# List all projects
nx show projects
# Filter by pattern (glob)
nx show projects --projects "apps/*"
nx show projects --projects "shared-*"
# Filter by project type
nx show projects --type app
nx show projects --type lib
nx show projects --type e2e
# Filter by target (projects that have a specific target)
nx show projects --withTarget build
nx show projects --withTarget e2e
# Find affected projects (changed since base branch)
nx show projects --affected
nx show projects --affected --base=main
nx show projects --affected --type app
# Combine filters
nx show projects --type lib --withTarget test
nx show projects --affected --exclude="*-e2e"
# Output as JSON
nx show projects --json
```
## Project Configuration
Use `nx show project <name> --json` to get the full resolved configuration for a project.
**Important**: Do NOT read `project.json` directly - it only contains partial configuration. The `nx show project` command returns the full resolved config including inferred targets from plugins.
You can read the full project schema at `node_modules/nx/schemas/project-schema.json` to understand nx project configuration options.
```bash
# Get full project configuration
nx show project my-app --json
# Extract specific parts from the JSON
nx show project my-app --json | jq '.targets'
nx show project my-app --json | jq '.targets.build'
nx show project my-app --json | jq '.targets | keys'
# Check project metadata
nx show project my-app --json | jq '{name, root, sourceRoot, projectType, tags}'
```
## Target Information
Targets define what tasks can be run on a project.
```bash
# List all targets for a project
nx show project my-app --json | jq '.targets | keys'
# Get full target configuration
nx show project my-app --json | jq '.targets.build'
# Check target executor/command
nx show project my-app --json | jq '.targets.build.executor'
nx show project my-app --json | jq '.targets.build.command'
# View target options
nx show project my-app --json | jq '.targets.build.options'
# Check target inputs/outputs (for caching)
nx show project my-app --json | jq '.targets.build.inputs'
nx show project my-app --json | jq '.targets.build.outputs'
# Find projects with a specific target
nx show projects --withTarget serve
nx show projects --withTarget e2e
```
## Workspace Configuration
Read `nx.json` directly for workspace-level configuration.
You can read the full project schema at `node_modules/nx/schemas/nx-schema.json` to understand nx project configuration options.
```bash
# Read the full nx.json
cat nx.json
# Or use jq for specific sections
cat nx.json | jq '.targetDefaults'
cat nx.json | jq '.namedInputs'
cat nx.json | jq '.plugins'
cat nx.json | jq '.generators'
```
Key nx.json sections:
- `targetDefaults` - Default configuration applied to all targets of a given name
- `namedInputs` - Reusable input definitions for caching
- `plugins` - Nx plugins and their configuration
- ...and much more, read the schema or nx.json for details
## Affected Projects
Find projects affected by changes in the current branch.
```bash
# Affected since base branch (auto-detected)
nx show projects --affected
# Affected with explicit base
nx show projects --affected --base=main
nx show projects --affected --base=origin/main
# Affected between two commits
nx show projects --affected --base=abc123 --head=def456
# Affected apps only
nx show projects --affected --type app
# Affected excluding e2e projects
nx show projects --affected --exclude="*-e2e"
# Affected by uncommitted changes
nx show projects --affected --uncommitted
# Affected by untracked files
nx show projects --affected --untracked
```
## Common Exploration Patterns
### "What's in this workspace?"
```bash
nx show projects
nx show projects --type app
nx show projects --type lib
```
### "How do I build/test/lint project X?"
```bash
nx show project X --json | jq '.targets | keys'
nx show project X --json | jq '.targets.build'
```
### "What depends on library Y?"
```bash
# Find projects that may depend on Y by searching for imports
# (Nx doesn't have a direct "dependents" command via CLI)
grep -r "from '@myorg/Y'" --include="*.ts" --include="*.tsx" apps/ libs/
```
### "What configuration options are available?"
```bash
cat node_modules/nx/schemas/nx-schema.json | jq '.properties | keys'
cat node_modules/nx/schemas/project-schema.json | jq '.properties | keys'
```
### "Why is project X affected?"
```bash
# Check what files changed
git diff --name-only main
# See which project owns those files
nx show project X --json | jq '.root'
```
+3 -4
View File
@@ -206,9 +206,8 @@ Fixes #ISSUE_NUMBER
- When running tasks (for example build, lint, test, e2e, etc.), always prefer running the task through `nx` (i.e. `nx run`, `nx run-many`, `nx affected`) instead of using the underlying tooling directly
- You have access to the Nx MCP server and its tools, use them to help the user
- When answering questions about the repository, use the `nx_workspace` tool first to gain an understanding of the workspace architecture where applicable.
- When working in individual projects, use the `nx_project_details` mcp tool to analyze and understand the specific project structure and dependencies
- For questions around nx configuration, best practices or if you're unsure, use the `nx_docs` tool to get relevant, up-to-date docs. Always use this instead of assuming things about nx configuration
- If the user needs help with an Nx configuration or project graph error, use the `nx_workspace` tool to get any errors
- For understanding the workspace structure, projects, or available tasks, use the `/nx-workspace` skill which provides guidance on exploring Nx workspaces
- For questions around nx configuration, best practices or if you're unsure, use the `nx_docs` MCP tool to get relevant, up-to-date docs. Always use this instead of assuming things about nx configuration
- For Nx plugin best practices, check `node_modules/@nx/<plugin>/PLUGIN.md`. Not all plugins have this file - proceed without it if unavailable.
<!-- nx configuration end-->
+3 -4
View File
@@ -206,9 +206,8 @@ Fixes #ISSUE_NUMBER
- When running tasks (for example build, lint, test, e2e, etc.), always prefer running the task through `nx` (i.e. `nx run`, `nx run-many`, `nx affected`) instead of using the underlying tooling directly
- You have access to the Nx MCP server and its tools, use them to help the user
- When answering questions about the repository, use the `nx_workspace` tool first to gain an understanding of the workspace architecture where applicable.
- When working in individual projects, use the `nx_project_details` mcp tool to analyze and understand the specific project structure and dependencies
- For questions around nx configuration, best practices or if you're unsure, use the `nx_docs` tool to get relevant, up-to-date docs. Always use this instead of assuming things about nx configuration
- If the user needs help with an Nx configuration or project graph error, use the `nx_workspace` tool to get any errors
- For understanding the workspace structure, projects, or available tasks, use the `/nx-workspace` skill which provides guidance on exploring Nx workspaces
- For questions around nx configuration, best practices or if you're unsure, use the `nx_docs` MCP tool to get relevant, up-to-date docs. Always use this instead of assuming things about nx configuration
- For Nx plugin best practices, check `node_modules/@nx/<plugin>/PLUGIN.md`. Not all plugins have this file - proceed without it if unavailable.
<!-- nx configuration end-->
+2 -11
View File
@@ -2,19 +2,10 @@
We would love for you to contribute to Nx! Read this document to see how to do it.
## How to Get Started Video
Watch this 5-minute video:
<a href="https://www.youtube.com/watch?v=8LCA_4qxc08" target="_blank" rel="noreferrer">
<p style="text-align: center;"><img src="./images/how-to-contribute.png" width="600" alt="Nx - How to contribute"></p>
</a>
## Got a Question?
We are trying to keep GitHub issues for bug reports and feature requests. Using the `nrwl` tag
on [Stack Overflow](https://stackoverflow.com/questions/tagged/nrwl) is a much better place to ask general questions
about how to use Nx.
We are trying to keep GitHub issues for bug reports and feature requests.
You can join our [Discord](https://go.nx.dev/community) for general questions and seeking help from others.
## Found an Issue?
Generated
+1405 -1428
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -1,6 +1,6 @@
(The MIT License)
Copyright (c) 2017-2025 Narwhal Technologies Inc.
Copyright (c) 2017-2026 Narwhal Technologies Inc.
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
+3 -5
View File
@@ -1,7 +1,7 @@
<p style="text-align: center;">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="./images/nx-dark.svg">
<img alt="Nx - Smart Repos · Fast Builds" src="./images/nx-light.svg" width="100%">
<img alt="Nx - Smart Monorepos · Fast Builds" src="./images/nx-light.svg" width="100%">
</picture>
</p>
@@ -19,9 +19,7 @@
<hr>
# Smart Repos · Fast Builds
Get to green PRs in half the time. Nx optimizes your builds, scales your CI, and fixes failed PRs. Built for developers and AI agents.
# The Monorepo Platform that amplifies both developers and AI agents. Nx optimizes your builds, scales your CI, and fixes failed PRs automatically. Ship in half the time.
Create a new Nx workspace with
@@ -58,7 +56,7 @@ Learn more in the [Nx CI docs &raquo;](https://nx.dev/ci/getting-started/intro?u
- [Our Twitter/X](https://x.com/nxdevtools)
<p style="text-align: center;"><a href="https://www.youtube.com/@nxdevtools/videos" target="_blank" rel="noreferrer"><img src="./images/nx-courses-and-videos.svg"
width="100%" alt="Nx - Smart Repos · Fast Builds"></a></p>
width="100%" alt="Nx - Smart Monorepos · Fast Builds"></a></p>
## Want to help?
+12
View File
@@ -13,3 +13,15 @@ Instead, please report them to the Security Team at security@nrwl.io.
You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message.
Nx follows the principle of Coordinated Vulnerability Disclosure.
## What Should Be Reported
The security email is for **demonstrable, verified vulnerabilities within the Nx codebase itself**.
**Please do not use the security email for:**
- Reports about outdated dependencies (e.g., "package X has a newer version available")
- Reports about dependencies with known CVEs that do not directly affect Nx functionality
- General vulnerability scanner output
If you have a concern about an outdated dependency that you believe impacts Nx users, please open a [GitHub issue](https://github.com/nrwl/nx/issues/new/choose) instead.
+42
View File
@@ -22,6 +22,48 @@ This documentation site leverages Astro's static site generation capabilities wi
- Dynamic API documentation generation from Nx packages and CLI commands
- Community plugin registry
## Information Architecture Principles
When creating or reorganizing documentation, follow these 5 principles to determine where content belongs.
### 1. Progressive Disclosure (The "Journey" Rule)
- **Concept:** Don't overwhelm the user. Reveal complexity only as they advance in their journey.
- **The Test:** _Is this for the First 30 Minutes (Getting Started), the First 30 Days (Features), or Forever (Reference)?_
### 2. Category Homogeneity (The "Scan" Rule)
- **Concept:** Items in a list must be of the same "type" (noun, verb, or concept) to reduce cognitive load.
- **The Test:** _Does this list mix Concepts (Mental Model), Tasks (Update Nx), and Products (React)? If yes, split it._
### 3. Type-Based Navigation (The "Intent" Rule)
- **Concept:** Separate **Learning** (Narrative/Guides) from **Looking Up** (Reference/API).
- **The Test:** _Is the user here to learn a workflow (Guide) or look up a flag syntax (Reference)?_
### 4. The Pen & Paper Test (The "Theory" Rule)
- **Concept:** Distinguish Architecture from Features to keep "Core Concepts" pure.
- **The Test:** _Can I explain this using only a pen and paper?_
- **Yes:** It goes in **How Nx Works** (Architecture).
- **No (I need a terminal):** It goes in **Platform Features** (Feature).
### 5. Universal vs. Specific (The "Placement" Rule)
- **Concept:** Distinguish Platform features from Ecosystem tools to prevent "Features" from becoming a junk drawer.
- **The Test:** _Does this feature apply to EVERY user (e.g., Caching, Agents)?_
- **Yes:** **Platform Features**.
- **No (Only React users):** **Technologies**.
### Sidebar Structure
The sidebar has 4 top-level sections that follow the user journey:
1. **Getting Started** - Essential setup, tutorials, and core concepts (How Nx Works, Platform Features)
2. **Technologies** - Framework and tool-specific guides (React, Angular, Node, build tools, test tools)
3. **Knowledge Base** - Recipes, troubleshooting, and topic-specific guides
4. **Reference** - Exhaustive facts, no narrative (CLI commands, configuration, API docs)
## Project Structure
```
+388
View File
@@ -0,0 +1,388 @@
# Nx Documentation Style Guide
This document defines the standards for Nx documentation on nx.dev, including voice, grammar, formatting, and terminology.
For automated enforcement, see the [Vale configuration](#vale-configuration) section.
## Information architecture
When creating or reorganizing documentation, follow these five principles to determine where content belongs.
### 1. Progressive disclosure (the "journey" rule)
Don't overwhelm the user. Reveal complexity only as they advance in their journey.
**The test:** Is this for the first 30 minutes (Getting Started), the first 30 days (Features), or forever (Reference)?
### 2. Category homogeneity (the "scan" rule)
Items in a list must be of the same "type" (noun, verb, or concept) to reduce cognitive load.
**The test:** Does this list mix concepts (mental model), tasks (update Nx), and products (React)? If yes, split it.
### 3. Type-based navigation (the "intent" rule)
Separate learning (narrative/guides) from looking up (reference/API).
**The test:** Is the user here to learn a workflow (guide) or look up a flag syntax (reference)?
### 4. The pen and paper test (the "theory" rule)
Distinguish architecture from features to keep "core concepts" pure.
**The test:** Can I explain this using only a pen and paper?
- Yes: It goes in **How Nx Works** (architecture).
- No (I need a terminal): It goes in **Platform Features** (feature).
### 5. Universal vs. specific (the "placement" rule)
Distinguish platform features from ecosystem tools to prevent "Features" from becoming a junk drawer.
**The test:** Does this feature apply to every user (e.g., caching, Nx Agents)?
- Yes: **Platform Features**.
- No (only React users): **Technologies**.
### Sidebar structure
The sidebar has four top-level sections that follow the user journey:
1. **Getting Started** - Essential setup, tutorials, and core concepts (How Nx Works, Platform Features)
2. **Technologies** - Framework and tool-specific guides (React, Angular, Node, build tools, test tools)
3. **Knowledge Base** - Recipes, troubleshooting, and topic-specific guides
4. **Reference** - Exhaustive facts, no narrative (CLI commands, configuration, API docs)
## The Nx voice
Nx documentation is **direct, practical, and confident**. We write like a knowledgeable colleague pairing with you — not like a textbook, not like a marketing page, and not like a chatbot.
The voice should be:
- **Conversational but efficient.** Use contractions. Get to the point. Don't pad sentences.
- **Second person.** Write "you" — address the reader directly.
- **Action-oriented.** Lead with what the reader can _do_, not what Nx _is_.
- **Honest about tradeoffs.** Don't oversell. If something has limitations, say so.
### Voice do's and don'ts
| Do | Don't |
| ------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------- |
| "You can speed up builds by enabling remote caching." | "Nx allows you to speed up builds." |
| "Run `nx build` to build your project." | "In order to build your project, you can run the `nx build` command." |
| "This works best with fewer than 50 projects." | "This feature can easily scale to any number of projects." |
| "Nx reads your `vite.config.ts` and infers build targets automatically." | "Nx provides a robust and comprehensive mechanism for inferring build targets." |
| "If the cache is stale, delete `.nx/cache` and retry." | "Should you encounter issues with caching, you may want to consider clearing your cache directory." |
### Anti-AI language
Documentation must not read like it was generated by an AI assistant. Even when AI tools are used in the writing process, the output must be edited to sound like a human wrote it.
**Never use these phrases:**
- "It's important to note that..."
- "It's worth noting that..." / "It should be noted that..."
- "In this section, we will explore..."
- "Let's dive into..." / "Let's take a closer look at..."
- "Whether you're a beginner or an experienced developer..."
- "In today's fast-paced development environment..."
- "Unlock the power of..." / "Harness the power of..."
- "Take your workspace to the next level"
- "Streamline your workflow" (as a generic claim without specifics)
- "This comprehensive guide will..."
- "Without further ado..."
- "In conclusion..." / "To summarize..." / "As we've seen..."
- "Game-changer" / "Cutting-edge" / "Groundbreaking"
- "Seamless" / "Seamlessly" (unless describing an actual integration)
**Avoid hedging words unless genuinely needed:**
- "Essentially" / "Basically" / "Effectively"
- "Generally speaking"
- "It is worth mentioning"
- "Arguably"
- "Needless to say"
- "As a matter of fact"
**Watch for AI-style sentence patterns:**
- Sentences that start with "This allows you to..." or "This enables you to..." — rewrite to lead with the reader's action.
- Paragraphs that start with a general claim and then restate it slightly differently. Say it once.
- Excessive use of "robust", "leverage", "utilize", "facilitate", "comprehensive", "aforementioned."
- Lists where every item starts with the same grammatical structure repeated 5+ times with slight variation. Vary your phrasing.
### Self-referential writing
Don't write about the document itself.
Do:
- "Nx uses a project graph to determine task dependencies."
Don't:
- "This page explains how Nx uses a project graph."
- "In this guide, we'll walk through..."
- "This document covers..."
Get right to the point. The reader already knows they're on a page — they want the information.
### Building trust
Don't use filler words that undermine the reader's trust.
- Don't use "easily", "simply", "just", or "straightforward" — if something were truly simple, you wouldn't need to document it. These words also make readers feel bad when they struggle.
- Don't use marketing language: "This feature will save you hours" or "Nx makes CI effortless."
- Be specific instead: "Remote caching can reduce CI times from 45 minutes to under 5 minutes for cache-hit builds."
### Customer perspective
Focus on what the reader can do, not what Nx does.
Do:
- "Use `nx affected` to run tasks only for projects impacted by your changes."
Don't:
- "Nx allows you to run affected tasks."
- "Nx provides the ability to run tasks selectively."
Words like "allow" and "enable" are signals you're writing from the product's perspective instead of the reader's.
## Language
Write in US English.
### Active voice
Use active voice in most cases.
Do: "Nx caches the build output."
Don't: "The build output is cached by Nx."
Exception: When "Nx" as the subject sounds awkward, passive voice is fine. "The output is stored in `.nx/cache`" is better than "Nx stores the output in `.nx/cache`" if Nx isn't the focus of the sentence.
### Contractions
Use contractions. They make the text feel natural.
- "You'll need to configure..." not "You will need to configure..."
- "It doesn't support..." not "It does not support..."
Don't contract for emphasis in warnings or error descriptions:
- "**Do not** delete the `nx.json` file."
- "Requests to localhost **are not** allowed."
Don't contract proper nouns: "the Vite plugin is..." not "Vite's a plugin..."
### Capitalization
Use sentence case for headings. Capitalize proper nouns only.
- `# Use remote caching to speed up CI`
- `## Configure the Vite plugin`
Feature names are lowercase unless they are a proper product name:
| Correct | Incorrect |
| -------------- | -------------- |
| remote caching | Remote Caching |
| task pipeline | Task Pipeline |
| project graph | Project Graph |
| Nx Cloud | nx cloud |
| Nx Console | nx console |
| Nx Agents | nx agents |
| Nx Replay | nx replay |
### Acronyms
Spell out acronyms on first use per page. Don't spell out widely-known ones: CI, CD, API, URL, CLI, PR, IDE.
Don't make acronyms plural with apostrophes. Use `APIs`, not `API's`.
### Numbers
Spell out zero through nine. Use numerals for 10 and above. Always use numerals with units: "5 minutes", "3 projects."
### Possessives
Don't use possessives on product names. "the Docker CLI", not "Docker's CLI." "the Nx configuration", not "Nx's configuration."
## Text
### Headings
- Don't skip heading levels (e.g., `##` to `####`).
- Don't use code in headings unless it's essential (like a CLI command).
- Don't use bold text in headings.
- Keep headings short and scannable. Lead with keywords.
### Line length
- Wrap lines at approximately 100 characters for readability in diffs.
- Start each new sentence on a new line.
- Exception: Don't break links across lines.
### Punctuation
- Use serial (Oxford) commas: "React, Angular, and Vue."
- Use one space between sentences.
- Don't use semicolons. Use two sentences instead.
- Don't use em dashes or en dashes. Use commas or separate sentences.
### Placeholder text
Use `<` and `>` for values the reader must replace:
```shell
nx run <project-name>:build
```
If the placeholder is inline, wrap it in a single backtick: `<your-project>`.
### Bold
Use bold for:
- UI elements: "Select **Add Connection**."
- Navigation paths: "Go to **Settings** > **Workspace**."
Don't use bold for emphasis or keywords. If you need emphasis, rewrite the sentence to be clearer.
### Inline code
Use inline code (single backticks) for:
- Commands and CLI arguments: `nx build`, `--parallel`
- File names and paths: `nx.json`, `.nx/cache`
- Configuration keys: `targetDefaults`, `namedInputs`
- Short outputs and values: `true`, `false`, `success`
### Code blocks
Use triple backticks with a language identifier:
````markdown
```json
{
"targetDefaults": {
"build": {
"cache": true
}
}
}
```
````
- Always specify a syntax language. Use `plaintext` if nothing else fits.
- Add a blank line before and after code blocks.
- For long config files, show only the relevant section and use comments to indicate omitted parts:
```json
{
// ... other config
"targetDefaults": {
"build": {
"cache": true
}
}
}
```
## Links
Links help readers find related information, but too many links make text hard to read.
### General rules
- Don't duplicate links. If you link to a page once, don't link to it again on the same page.
- Don't use links in headings.
- Avoid more than 15 links to other pages on any single page.
- Avoid multiple links in a single paragraph when possible.
### Link text
Use descriptive text, not "here" or "this page."
Do:
- "For more information, see [remote caching](/features/cache)."
- "To configure task pipelines, see [task pipeline configuration](/concepts/task-pipeline-configuration)."
Don't:
- "For more information, see [this page](/features/cache)."
- "Click [here](/features/cache) to learn more."
- "For more information, see the [Remote Caching](/features/cache) documentation."
Standard patterns:
- `For more information, see [link text](url).`
- `To <do this thing>, see [link text](url).`
### External links
Minimize external links. They break over time and are hard to maintain. When you must link externally, prefer official documentation (e.g., Vite docs, Webpack docs) over blog posts or third-party guides.
## Lists
- Use ordered lists for sequences of steps.
- Use unordered lists when order doesn't matter.
- Use dashes (`-`) for unordered lists.
- Start ordered list items with `1.` (Markdown auto-increments).
- Make list items parallel in structure.
- Add a colon after the introductory phrase.
- Don't use list items to complete an introductory sentence.
Do:
```markdown
You can clear the cache in the following ways:
- Delete the `.nx/cache` directory manually.
- Run `nx reset` to clear all cached results.
```
Don't:
```markdown
You can clear the cache by:
- Deleting the `.nx/cache` directory manually.
- Running `nx reset`.
```
## Tables
Use tables for structured data that benefits from a matrix layout. For simple lists of items with descriptions, use a regular list instead.
- Don't leave cells empty. Use "N/A" or "None."
- Use sentence case for headers.
- Keep the header and delimiter rows the same length.
## Nx-specific terminology
Use these terms consistently. When writing about Nx concepts, use the exact term from this list.
| Term | Usage notes |
| -------------- | ------------------------------------------------------------------------------------------------- |
| workspace | The root directory managed by Nx. Not "repo" or "monorepo" when referring to Nx's context. |
| project | An app or library within the workspace. |
| target | A task that can be run for a project (e.g., `build`, `test`, `lint`). |
| executor | The implementation behind a target. Not "builder." |
| generator | Code scaffolding tool. Not "schematic." |
| plugin | An Nx plugin that provides executors, generators, or graph inference. |
| task | A specific invocation of a target for a project (e.g., `myapp:build`). |
| task pipeline | The dependency graph between tasks. Not "task orchestration" or "task graph" in user-facing docs. |
| project graph | The dependency graph between projects. |
| affected | Projects impacted by a code change. |
| cache / cached | Not "memoized" or "stored results." |
| remote caching | Sharing cached results across machines. Specific product: "Nx Replay." |
| Nx Cloud | The hosted CI/CD product. Always capitalized. |
| Nx Console | The IDE extension. Always capitalized. |
| Nx Agents | Distributed task execution product. Always capitalized. |
| Nx Replay | Remote caching product. Always capitalized. |
| `nx.json` | Always in code style. |
| `project.json` | Always in code style. |
+10 -24
View File
@@ -6,14 +6,17 @@ import react from '@astrojs/react';
import markdoc from '@astrojs/markdoc';
import tailwindcss from '@tailwindcss/vite';
import { sidebar } from './sidebar.mts';
import rehypeTableOptionLinks from './src/plugins/utils/rehype-table-option-links.ts';
import { resolveNxDevUrl } from './src/utils/resolve-nx-dev-url.ts';
// Always resolve NX_DEV_URL so downstream consumers (Footer, Header) pick it up.
// For deploy previews this overrides any site-level env var to point to the matching preview.
process.env.NX_DEV_URL = resolveNxDevUrl();
const BASE = '/docs';
// This is exposed as window.__CONFIG
const PUBLIC_CONFIG = {
cookiebotDisabled: process.env.COOKIEBOT_DISABLED === 'true',
cookiebotId: process.env.COOKIEBOT_ID ?? null,
gaMeasurementId: 'UA-88380372-10',
gtmMeasurementId: 'GTM-KW8423B6',
isProd: process.env.NODE_ENV === 'production',
};
@@ -33,6 +36,9 @@ export default defineConfig({
},
},
},
markdown: {
rehypePlugins: [rehypeTableOptionLinks],
},
trailingSlash: 'never',
// This adapter doesn't support local previews, so only load it on Netlify.
adapter: process.env['NETLIFY'] ? netlify() : undefined,
@@ -56,22 +62,6 @@ export default defineConfig({
tag: 'script',
content: `window.__CONFIG = ${JSON.stringify(PUBLIC_CONFIG)};`,
},
...(process.env.COOKIEBOT_ID &&
process.env.COOKIEBOT_DISABLED !== 'true'
? [
{
/** @type {"script"} */
tag: 'script',
attrs: {
id: 'Cookiebot',
src: 'https://consent.cookiebot.com/uc.js',
'data-cbid': process.env.COOKIEBOT_ID,
'data-blockingmode': 'auto',
type: 'text/javascript',
},
},
]
: []),
{
tag: 'script',
attrs: {
@@ -87,17 +77,13 @@ export default defineConfig({
// since the sidebar doesn't auto generate w/ dynamic routes from src/pages/reference
// only the src/content/docs/reference files
'./src/plugins/sidebar-reference-updater.middleware.ts',
'./src/plugins/sidebar-icons.middleware.ts',
'./src/plugins/og.middleware.ts',
'./src/plugins/github-stars.middleware.ts',
'./src/plugins/raw-content.middleware.ts',
'./src/plugins/canonical.middleware.ts',
],
markdown: {
// this breaks the renderMarkdown function in the plugin loader due to starlight path normalization
// as to _why_ it has to normalize a path?
// idk just working around the issue for now but we'll want to have linked headers so will need to fix
headingLinks: false,
headingLinks: true,
},
social: [
{ icon: 'github', label: 'GitHub', href: 'https://github.com/nrwl/nx' },
@@ -0,0 +1,129 @@
import { test, expect } from '@playwright/test';
test.describe('CLI sub-command formatting', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/docs/reference/nx-commands');
await expect(
page.getByRole('heading', { name: 'Nx Commands' })
).toBeVisible();
});
test('parent commands render as h2 and sub-commands as h3', async ({
page,
}) => {
const mainContent = page.getByTestId('main-pane');
// "nx show" should be an h2 (top-level parent command)
const showHeading = mainContent.getByRole('heading', {
name: 'nx show',
level: 2,
exact: true,
});
await expect(showHeading).toBeVisible();
// "nx show projects" should be an h3 (sub-command nested under parent)
const showProjectsHeading = mainContent.getByRole('heading', {
name: 'nx show projects',
level: 3,
exact: true,
});
await expect(showProjectsHeading).toBeVisible();
// "nx show project" should also be an h3
const showProjectHeading = mainContent.getByRole('heading', {
name: 'nx show project',
level: 3,
exact: true,
});
await expect(showProjectHeading).toBeVisible();
});
test('sub-command usage blocks show the full command name', async ({
page,
}) => {
const mainContent = page.getByTestId('main-pane');
// Find the "nx show projects" section and verify its usage block
// The usage code block should contain "nx show projects", not "nx projects"
const showProjectsHeading = mainContent.getByRole('heading', {
name: 'nx show projects',
level: 3,
exact: true,
});
await expect(showProjectsHeading).toBeVisible();
// Get the section between "nx show projects" heading and the next heading.
// We look for a code block containing the correct usage pattern.
const codeBlocks = mainContent.locator('pre code');
const allCodeTexts = await codeBlocks.allTextContents();
// There should be a usage block with "nx show projects" (full sub-command name)
expect(allCodeTexts.some((text) => text.includes('nx show projects'))).toBe(
true
);
// There should NOT be a usage block with just "nx projects" (missing parent)
expect(
allCodeTexts.some(
(text) => text.match(/^nx projects/) || text.match(/\nnx projects/)
)
).toBe(false);
});
test('release sub-commands use correct heading and usage format', async ({
page,
}) => {
const mainContent = page.getByTestId('main-pane');
// "nx release" should be h2
const releaseHeading = mainContent.getByRole('heading', {
name: 'nx release',
level: 2,
exact: true,
});
await expect(releaseHeading).toBeVisible();
// "nx release version" should be h3
const releaseVersionHeading = mainContent.getByRole('heading', {
name: 'nx release version',
level: 3,
exact: true,
});
await expect(releaseVersionHeading).toBeVisible();
// Verify usage block includes the full command
const codeBlocks = mainContent.locator('pre code');
const allCodeTexts = await codeBlocks.allTextContents();
expect(
allCodeTexts.some((text) => text.includes('nx release version'))
).toBe(true);
});
test('options and examples are h4 headings excluded from the TOC', async ({
page,
}) => {
const mainContent = page.getByTestId('main-pane');
// Options/Examples should be h4 headings — linkable but below the TOC threshold (h2-h3)
const sharedOptionsHeading = mainContent.getByRole('heading', {
name: 'Shared Options',
level: 4,
});
await expect(sharedOptionsHeading.first()).toBeVisible();
const optionsHeading = mainContent.getByRole('heading', {
name: 'Options',
level: 4,
});
await expect(optionsHeading.first()).toBeVisible();
// They should NOT appear as h2 or h3 (which would put them in the TOC)
await expect(
mainContent.getByRole('heading', { name: 'Options', level: 2 })
).toHaveCount(0);
await expect(
mainContent.getByRole('heading', { name: 'Options', level: 3 })
).toHaveCount(0);
});
});
+1 -1
View File
@@ -11,7 +11,7 @@ test('links in descriptions of properties should correctly link to the same page
await page
.getByTestId('main-pane')
.getByRole('link', { name: 'nxCloudAccessToken' })
.getByRole('link', { name: 'nxCloudAccessToken', exact: true })
.click();
await expect(
+15
View File
@@ -4,9 +4,15 @@ import {
Markdoc,
} from '@astrojs/markdoc/config';
import starlightMarkdoc from '@astrojs/starlight-markdoc';
import { transformOptionsTable } from './src/utils/markdoc-table-option-links';
export default defineMarkdocConfig({
extends: [starlightMarkdoc()],
nodes: {
table: {
transform: transformOptionsTable,
},
},
tags: {
call_to_action: {
render: component('./src/components/markdoc/CallToAction.astro'),
@@ -238,6 +244,15 @@ export default defineMarkdocConfig({
},
},
},
sidebar_group_cards: {
render: component('./src/components/markdoc/SidebarGroupCards.astro'),
attributes: {
group: {
type: 'String',
required: true,
},
},
},
metrics: {
render: component('./src/components/markdoc/Metrics.astro'),
attributes: {
+3
View File
@@ -4,6 +4,9 @@
NX_GRADLE_DISABLE = "true"
NX_MAVEN_DISABLE = "true"
# Edge functions are auto-discovered from netlify/edge-functions/
# Path configuration is in each function's inline `config` export
# Permanent redirects (301 by default)
# Storybook docs consolidation
@@ -0,0 +1,59 @@
import type { Context } from 'https://edge.netlify.com';
/**
* Content negotiation for LLM-friendly docs access.
* See: https://llmstxt.org/
*/
export default async function handler(
request: Request,
context: Context
): Promise<Response | URL> {
const url = new URL(request.url);
const pathname = url.pathname;
const acceptHeader = request.headers.get('accept') || '';
// Serve markdown for LLM tools that explicitly request it
// Or if there are no accept headers passed (e.g. Cursor)
if (!acceptHeader || acceptHeader.includes('text/markdown')) {
const mdPath = pathname.replace(/\/?$/, '.md');
return new URL(mdPath, request.url);
}
const response = await context.next();
const contentType = response.headers.get('content-type') || '';
if (!contentType.includes('text/html')) {
return response;
}
const mdPath = pathname.replace(/\/?$/, '.md');
const linkHeader = [
`<${mdPath}>; rel="alternate"; type="text/markdown"`,
`</docs/llms.txt>; rel="alternate"; type="text/markdown"; title="LLM Index"`,
`</docs/llms-full.txt>; rel="alternate"; type="text/markdown"; title="Full Documentation"`,
].join(', ');
// Netlify responses are immutable
const newHeaders = new Headers(response.headers);
newHeaders.set('Link', linkHeader);
return new Response(response.body, {
status: response.status,
statusText: response.statusText,
headers: newHeaders,
});
}
export const config = {
path: ['/docs/*'],
excludedPath: [
'/docs/*.md',
'/docs/*.js',
'/docs/*.txt',
'/docs/images/*',
// _astro and other asset paths
'/docs/_*',
],
};
@@ -0,0 +1,118 @@
import type { Context } from 'https://edge.netlify.com';
// Configuration - set these in Netlify environment variables
const GA_MEASUREMENT_ID =
Netlify.env.get('GA_MEASUREMENT_ID') || 'G-XXXXXXXXXX';
const GA_API_SECRET = Netlify.env.get('GA_API_SECRET') || '';
function getClientId(request: Request): string {
// Try to extract existing GA client ID from cookie
const cookies = request.headers.get('cookie') || '';
const gaMatch = cookies.match(/_ga=GA\d+\.\d+\.(\d+\.\d+)/);
if (gaMatch) {
return gaMatch[1];
}
// Generate a new client ID for this request
// For non-browser clients (AI tools), this creates a session-based ID
const timestamp = Date.now();
const random = Math.floor(Math.random() * 1000000000);
return `${random}.${timestamp}`;
}
async function sendToGA4(
request: Request,
context: Context,
pathname: string
): Promise<void> {
if (!GA_API_SECRET) {
console.warn('GA_API_SECRET not configured, skipping analytics');
return;
}
const clientId = getClientId(request);
const userAgent = request.headers.get('user-agent') || 'unknown';
// Anthropic: ClaudeBot (training), Claude-User (user fetch), Claude-SearchBot (search index),
// Claude-Web (web crawler), anthropic-ai (legacy training)
// OpenAI: GPTBot (training), ChatGPT-User (user browsing), OAI-SearchBot (search index)
// Perplexity: PerplexityBot (search index), Perplexity-User (user fetch)
// Google: Google-Extended (AI/Gemini training)
// Other: Bytespider (ByteDance training)
const isAITool =
/ClaudeBot|Claude-User|Claude-SearchBot|Claude-Web|anthropic-ai|GPTBot|ChatGPT-User|OAI-SearchBot|PerplexityBot|Perplexity-User|Google-Extended|Bytespider/i.test(
userAgent
);
// Generic bots (SEO crawlers, social previews, etc.)
const isGenericBot =
/Googlebot|Amazonbot|CCBot|BingBot|YandexBot|DuckDuckBot|Applebot|crawler|spider|slurp|facebook|twitter|linkedin|slack|discord|telegram/i.test(
userAgent
);
const payload = {
client_id: clientId,
events: [
{
name: 'server_page_view',
params: {
page_location: request.url,
page_title: pathname,
page_path: pathname,
// Custom parameters for filtering
content_type: pathname.endsWith('.txt')
? 'text/plain'
: 'text/markdown',
file_extension: pathname.substring(pathname.lastIndexOf('.')),
user_agent: userAgent,
is_ai_tool: isAITool ? 'true' : 'false',
is_bot: isGenericBot ? 'true' : 'false',
country: context.geo?.country?.code || 'unknown',
},
},
],
};
console.log(`Tracked asset path: ${pathname}`);
const endpoint = `https://www.google-analytics.com/mp/collect?measurement_id=${GA_MEASUREMENT_ID}&api_secret=${GA_API_SECRET}`;
try {
await fetch(endpoint, {
method: 'POST',
body: JSON.stringify(payload),
});
} catch (error) {
// Log but don't fail the request
console.error('Failed to send to GA4:', error);
}
}
export default async function handler(
request: Request,
context: Context
): Promise<Response> {
const url = new URL(request.url);
const pathname = url.pathname;
// Send analytics in background (non-blocking)
context.waitUntil(sendToGA4(request, context, pathname));
// Continue to serve the actual file
const response = await context.next();
// Netlify Edge Function responses are immutable, so create a new Response
const newHeaders = new Headers(response.headers);
newHeaders.set('x-nx-edge-function', 'track-asset-requests');
return new Response(response.body, {
status: response.status,
statusText: response.statusText,
headers: newHeaders,
});
}
export const config = {
path: ['/**/*.txt', '/**/*.md'],
// Something is adding .png.md and .svg.md to get image paths, exclude those.
excludedPath: ['/docs/og/*', '/docs/*.svg.md', '/docs/*.png.md'],
};
@@ -0,0 +1,128 @@
import type { Context } from 'https://edge.netlify.com';
const GA_MEASUREMENT_ID =
Netlify.env.get('GA_MEASUREMENT_ID') || 'G-XXXXXXXXXX';
const GA_API_SECRET = Netlify.env.get('GA_API_SECRET') || '';
function getClientId(request: Request): string {
const cookies = request.headers.get('cookie') || '';
const gaMatch = cookies.match(/_ga=GA\d+\.\d+\.(\d+\.\d+)/);
if (gaMatch) return gaMatch[1];
const timestamp = Date.now();
const random = Math.floor(Math.random() * 1000000000);
return `${random}.${timestamp}`;
}
async function sendToGA4(
request: Request,
context: Context,
pathname: string
): Promise<void> {
if (!GA_API_SECRET) {
console.warn('GA_API_SECRET not configured, skipping analytics');
return;
}
const clientId = getClientId(request);
const userAgent = request.headers.get('user-agent') || 'unknown';
// Anthropic: ClaudeBot (training), Claude-User (user fetch), Claude-SearchBot (search index),
// Claude-Web (web crawler), anthropic-ai (legacy training)
// OpenAI: GPTBot (training), ChatGPT-User (user browsing), OAI-SearchBot (search index)
// Perplexity: PerplexityBot (search index), Perplexity-User (user fetch)
// Google: Google-Extended (AI/Gemini training)
// Other: Bytespider (ByteDance training)
const isAITool =
/ClaudeBot|Claude-User|Claude-SearchBot|Claude-Web|anthropic-ai|GPTBot|ChatGPT-User|OAI-SearchBot|PerplexityBot|Perplexity-User|Google-Extended|Bytespider/i.test(
userAgent
);
// Generic bots (SEO crawlers, social previews, etc.)
const isGenericBot =
/Googlebot|Amazonbot|CCBot|BingBot|YandexBot|DuckDuckBot|Applebot|crawler|spider|slurp|facebook|twitter|linkedin|slack|discord|telegram/i.test(
userAgent
);
const payload = {
client_id: clientId,
events: [
{
name: 'server_page_view',
params: {
page_location: request.url,
page_title: pathname,
page_path: pathname,
content_type: 'text/html',
file_extension: '.html',
user_agent: userAgent,
is_ai_tool: isAITool ? 'true' : 'false',
is_bot: isGenericBot ? 'true' : 'false',
country: context.geo?.country?.code || 'unknown',
},
},
],
};
console.log(`Tracked HTML page: ${pathname}`);
const endpoint = `https://www.google-analytics.com/mp/collect?measurement_id=${GA_MEASUREMENT_ID}&api_secret=${GA_API_SECRET}`;
try {
await fetch(endpoint, {
method: 'POST',
body: JSON.stringify(payload),
});
} catch (error) {
console.error('Failed to send to GA4:', error);
}
}
export default async function handler(
request: Request,
context: Context
): Promise<Response> {
const pathname = new URL(request.url).pathname;
// Always track - filtering is done at config level via `accept: ['text/html']`
context.waitUntil(sendToGA4(request, context, pathname));
const response = await context.next();
const newHeaders = new Headers(response.headers);
newHeaders.set('x-nx-edge-function', 'track-page-requests');
return new Response(response.body, {
status: response.status,
statusText: response.statusText,
headers: newHeaders,
});
}
export const config = {
path: ['/docs/*'],
// Only track requests from clients that want HTML (browsers)
// This filters out curl, AI agents, and other non-browser clients
accept: ['text/html'],
excludedPath: [
// Text/code files (handled by track-asset-requests or not tracked)
'/docs/*.md',
'/docs/*.js',
'/docs/*.txt',
// Images
'/docs/*.svg',
'/docs/*.png',
'/docs/*.jpg',
'/docs/*.jpeg',
'/docs/*.gif',
'/docs/*.webp',
'/docs/*.ico',
'/docs/images/*',
'/docs/og/*',
// Fonts
'/docs/fonts/*',
'/docs/*.woff',
'/docs/*.woff2',
// Search index (pagefind)
'/docs/pagefind/*',
// Astro build assets
'/docs/_*',
],
};
+1
View File
@@ -16,6 +16,7 @@
"@nx/nx-dev-ui-icons": "workspace:*",
"@nx/nx-dev-ui-markdoc": "workspace:*",
"@tailwindcss/vite": "^4.1.11",
"@types/hast": "^3.0.4",
"astro": "^5.10.1",
"astro-og-canvas": "^0.7.0",
"canvaskit-wasm": "^0.40.0",
+77 -111
View File
@@ -38,31 +38,9 @@
const config = window.__CONFIG || {};
if (!config.isProd) return;
const isCookiebotDisabled = config.cookiebotDisabled ?? false;
const gaMeasurementId = config.gaMeasurementId ?? 'UA-88380372-10';
const gtmMeasurementId = config.gtmMeasurementId ?? 'GTM-KW8423B6';
// Initialize global objects
window.Cookiebot = window.Cookiebot || {};
window.dataLayer = window.dataLayer || [];
window.gtag =
window.gtag ||
function () {
window.dataLayer.push(arguments);
};
const loadGoogleAnalytics = () => {
const script = document.createElement('script');
script.src = `https://www.googletagmanager.com/gtag/js?id=${gaMeasurementId}`;
script.async = true;
document.head.appendChild(script);
// Initialize gtag
window.gtag('js', new Date());
window.gtag('config', gaMeasurementId, {
page_path: window.location.pathname,
});
};
const loadGTM = () => {
if (!gtmMeasurementId) return;
@@ -79,70 +57,82 @@
})(window, document, 'script', 'dataLayer', gtmMeasurementId);
};
const loadHubSpot = () => {
const hsScript = document.createElement('script');
hsScript.src = 'https://js.hs-scripts.com/2757427.js';
hsScript.async = true;
hsScript.defer = true;
document.head.appendChild(hsScript);
// Load HubSpot Forms
const hsFormsScript = document.createElement('script');
hsFormsScript.src = '//js.hsforms.net/forms/v2.js';
hsFormsScript.async = true;
hsFormsScript.defer = true;
document.head.appendChild(hsFormsScript);
// GA4 events are dispatched via GTM dataLayer.
const pushGtmEvent = (eventName, payload) => {
window.dataLayer.push({ event: eventName, ...payload });
};
const loadApollo = () => {
const n = Math.random().toString(36).substring(7);
const script = document.createElement('script');
script.src = `https://assets.apollo.io/micro/website-tracker/tracker.iife.js?nocache=${n}`;
script.async = true;
script.defer = true;
script.onload = function () {
if (window.trackingFunctions?.onLoad) {
window.trackingFunctions.onLoad({
appId: '65e1db2f1976f30300fd8b26',
// Scroll depth tracking
const SCROLL_THRESHOLDS = [10, 25, 50, 75, 90];
let firedThresholds = new Set();
let scrollTrackingEnabled = false;
let scrollRafId = null;
function getScrollPercentage() {
const scrollTop = window.scrollY || document.documentElement.scrollTop;
const scrollHeight = document.documentElement.scrollHeight;
const clientHeight = window.innerHeight;
return (scrollTop + clientHeight) / scrollHeight;
}
function handleScrollTracking() {
if (!scrollTrackingEnabled) return;
const scrollPercentage = getScrollPercentage() * 100;
// Fire events for all thresholds we've passed but haven't fired yet
for (const threshold of SCROLL_THRESHOLDS) {
if (scrollPercentage >= threshold && !firedThresholds.has(threshold)) {
firedThresholds.add(threshold);
sendSearchEvent(`scroll_${threshold}`, {
event_category: 'scroll',
event_label: window.location.pathname,
});
}
};
document.head.appendChild(script);
};
}
}
const loadHotjar = () => {
(function (h, o, t, j, a, r) {
h.hj =
h.hj ||
function () {
(h.hj.q = h.hj.q || []).push(arguments);
};
h._hjSettings = { hjid: 2774127, hjsv: 6 };
a = o.getElementsByTagName('head')[0];
r = o.createElement('script');
r.async = 1;
r.src = t + h._hjSettings.hjid + j + h._hjSettings.hjsv;
a.appendChild(r);
})(window, document, 'https://static.hotjar.com/c/hotjar-', '.js?sv=');
};
function throttledScrollHandler() {
if (scrollRafId !== null) return;
const loadTwitterPixel = () => {
!(function (e, t, n, s, u, a) {
e.twq ||
((s = e.twq =
function () {
s.exe ? s.exe.apply(s, arguments) : s.queue.push(arguments);
}),
(s.version = '1.1'),
(s.queue = []),
(u = t.createElement(n)),
(u.async = !0),
(u.src = 'https://static.ads-twitter.com/uwt.js'),
(a = t.getElementsByTagName(n)[0]),
a.parentNode.insertBefore(u, a));
})(window, document, 'script');
window.twq('config', 'obtp4');
};
scrollRafId = requestAnimationFrame(() => {
handleScrollTracking();
scrollRafId = null;
});
}
function attachScrollListener() {
window.addEventListener('scroll', throttledScrollHandler, {
passive: true,
});
}
function setupScrollTracking() {
// Reset scroll depth on navigation (for SPA-like behavior via View Transitions)
firedThresholds = new Set();
scrollTrackingEnabled = false;
// Delay tracking start to avoid false triggers during navigation
setTimeout(() => {
scrollTrackingEnabled = true;
// Immediately check current scroll position to capture thresholds
// that may have been passed during the delay
handleScrollTracking();
}, 500);
attachScrollListener();
// Handle Astro View Transitions - reset on navigation
document.addEventListener('astro:after-swap', () => {
firedThresholds = new Set();
scrollTrackingEnabled = false;
setTimeout(() => {
scrollTrackingEnabled = true;
// Immediately check current scroll position after navigation
handleScrollTracking();
}, 500);
});
}
const SEARCH_DEBOUNCE_MS = 1000;
let searchDebounceTimer;
@@ -151,8 +141,7 @@
let inputHandler = null;
function sendSearchEvent(eventType, data) {
if (typeof window.gtag !== 'undefined')
window.gtag('event', eventType, data);
pushGtmEvent(eventType, data);
}
function trackSearchQuery(query) {
@@ -203,31 +192,11 @@
});
}
const checkAndLoadScripts = () => {
if (isCookiebotDisabled) {
loadGoogleAnalytics();
loadGTM();
loadHubSpot();
setupSearchTracking();
} else if (window.Cookiebot && window.Cookiebot.consent) {
// Statistics cookies (Google Analytics, GTM, Search Tracking)
if (window.Cookiebot.consent.statistics) {
loadGoogleAnalytics();
loadGTM();
setupSearchTracking();
}
// Marketing cookies (HubSpot, Apollo, Hotjar, Twitter)
if (window.Cookiebot.consent.marketing) {
loadHubSpot();
loadApollo();
loadHotjar();
loadTwitterPixel();
}
} else {
// Wait for Cookiebot to load
setTimeout(checkAndLoadScripts, 100);
}
const initializeAnalytics = () => {
if (!gtmMeasurementId) return;
loadGTM();
setupSearchTracking();
setupScrollTracking();
};
// Add GTM noscript iframe to body
@@ -244,11 +213,8 @@
document.body.insertBefore(noscript, document.body.firstChild);
};
// Listen for user consent to cookies
window.addEventListener('CookiebotOnAccept', checkAndLoadScripts);
// Initial check
checkAndLoadScripts();
initializeAnalytics();
// Add GTM noscript on DOM ready
if (document.readyState === 'loading') {
+1137 -180
View File
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,122 @@
<svg width="980" height="720" viewBox="0 0 980 720" fill="none" xmlns="http://www.w3.org/2000/svg">
<!-- Defs: arrow markers -->
<defs>
<marker id="arrowBlue" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto" markerUnits="strokeWidth">
<polygon points="0 0, 10 3.5, 0 7" fill="#476088"/>
</marker>
<marker id="arrowGreen" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto" markerUnits="strokeWidth">
<polygon points="0 0, 10 3.5, 0 7" fill="#70AF40"/>
</marker>
</defs>
<!-- Outer dotted border: Synthetic Monorepo boundary -->
<rect x="40" y="40" width="900" height="590" rx="24" ry="24"
stroke="#94A3B8" stroke-width="3" stroke-dasharray="12 6" fill="none"/>
<!-- "Synthetic Monorepo" label at bottom inside box -->
<text x="490" y="665" text-anchor="middle" font-family="system-ui, -apple-system, sans-serif" font-size="26" font-weight="700" fill="#94A3B8">Synthetic Monorepo</text>
<!-- ============================================ -->
<!-- Monorepo A (top-left): a rounded rect with 3 internal projects -->
<!-- ============================================ -->
<rect x="80" y="70" width="260" height="200" rx="16" ry="16"
stroke="#9CA3AF" stroke-width="2" fill="none"/>
<text x="210" y="100" text-anchor="middle" font-family="system-ui, -apple-system, sans-serif" font-size="14" font-weight="600" fill="#9CA3AF">Monorepo A</text>
<!-- App 1 (blue) -->
<circle cx="140" cy="165" r="38" fill="#476088"/>
<text x="140" y="161" text-anchor="middle" font-family="system-ui, -apple-system, sans-serif" font-size="13" font-weight="600" fill="white">App 1</text>
<text x="140" y="178" text-anchor="middle" font-family="system-ui, -apple-system, sans-serif" font-size="11" fill="#CBD5E1">frontend</text>
<!-- Lib A (green) -->
<circle cx="270" cy="145" r="32" fill="#70AF40"/>
<text x="270" y="141" text-anchor="middle" font-family="system-ui, -apple-system, sans-serif" font-size="13" font-weight="600" fill="white">Lib A</text>
<text x="270" y="157" text-anchor="middle" font-family="system-ui, -apple-system, sans-serif" font-size="11" fill="#E8F5E9">utils</text>
<!-- Lib B (green) -->
<circle cx="248" cy="222" r="28" fill="#70AF40"/>
<text x="248" y="218" text-anchor="middle" font-family="system-ui, -apple-system, sans-serif" font-size="12" font-weight="600" fill="white">Lib B</text>
<text x="248" y="233" text-anchor="middle" font-family="system-ui, -apple-system, sans-serif" font-size="10" fill="#E8F5E9">ui</text>
<!-- Internal arrows within Monorepo A -->
<line x1="175" y1="155" x2="238" y2="143" stroke="#9CA3AF" stroke-width="1.5" marker-end="url(#arrowBlue)" opacity="0.4"/>
<line x1="165" y1="190" x2="225" y2="212" stroke="#9CA3AF" stroke-width="1.5" marker-end="url(#arrowBlue)" opacity="0.4"/>
<!-- ============================================ -->
<!-- Monorepo B (right): a rounded rect with 2 internal projects -->
<!-- ============================================ -->
<rect x="640" y="70" width="260" height="200" rx="16" ry="16"
stroke="#9CA3AF" stroke-width="2" fill="none"/>
<text x="770" y="100" text-anchor="middle" font-family="system-ui, -apple-system, sans-serif" font-size="14" font-weight="600" fill="#9CA3AF">Monorepo B</text>
<!-- App 2 (blue) -->
<circle cx="710" cy="170" r="38" fill="#476088"/>
<text x="710" y="166" text-anchor="middle" font-family="system-ui, -apple-system, sans-serif" font-size="13" font-weight="600" fill="white">App 2</text>
<text x="710" y="183" text-anchor="middle" font-family="system-ui, -apple-system, sans-serif" font-size="11" fill="#CBD5E1">backend</text>
<!-- Lib C (green) -->
<circle cx="840" cy="185" r="32" fill="#70AF40"/>
<text x="840" y="181" text-anchor="middle" font-family="system-ui, -apple-system, sans-serif" font-size="13" font-weight="600" fill="white">Lib C</text>
<text x="840" y="197" text-anchor="middle" font-family="system-ui, -apple-system, sans-serif" font-size="11" fill="#E8F5E9">auth</text>
<!-- Internal arrow within Monorepo B -->
<line x1="745" y1="175" x2="808" y2="182" stroke="#9CA3AF" stroke-width="1.5" marker-end="url(#arrowBlue)" opacity="0.4"/>
<!-- ============================================ -->
<!-- Standalone repos -->
<!-- ============================================ -->
<!-- Lib D (green, standalone, CENTER - pushed down to middle row) -->
<circle cx="490" cy="340" r="38" fill="#70AF40"/>
<text x="490" y="336" text-anchor="middle" font-family="system-ui, -apple-system, sans-serif" font-size="13" font-weight="600" fill="white">Lib D</text>
<text x="490" y="352" text-anchor="middle" font-family="system-ui, -apple-system, sans-serif" font-size="11" fill="#E8F5E9">shared</text>
<text x="490" y="390" text-anchor="middle" font-family="system-ui, -apple-system, sans-serif" font-size="11" font-weight="500" fill="#9CA3AF">standalone repo</text>
<!-- App 3 (blue, standalone, bottom-left) -->
<circle cx="200" cy="500" r="42" fill="#476088"/>
<text x="200" y="496" text-anchor="middle" font-family="system-ui, -apple-system, sans-serif" font-size="13" font-weight="600" fill="white">App 3</text>
<text x="200" y="513" text-anchor="middle" font-family="system-ui, -apple-system, sans-serif" font-size="11" fill="#CBD5E1">mobile</text>
<text x="200" y="554" text-anchor="middle" font-family="system-ui, -apple-system, sans-serif" font-size="11" font-weight="500" fill="#9CA3AF">standalone repo</text>
<!-- Lib E (green, standalone, bottom-center) -->
<circle cx="490" cy="520" r="32" fill="#70AF40"/>
<text x="490" y="516" text-anchor="middle" font-family="system-ui, -apple-system, sans-serif" font-size="13" font-weight="600" fill="white">Lib E</text>
<text x="490" y="532" text-anchor="middle" font-family="system-ui, -apple-system, sans-serif" font-size="11" fill="#E8F5E9">design</text>
<text x="490" y="564" text-anchor="middle" font-family="system-ui, -apple-system, sans-serif" font-size="11" font-weight="500" fill="#9CA3AF">standalone repo</text>
<!-- App 4 (blue, standalone, bottom-right) -->
<circle cx="770" cy="500" r="42" fill="#476088"/>
<text x="770" y="496" text-anchor="middle" font-family="system-ui, -apple-system, sans-serif" font-size="13" font-weight="600" fill="white">App 4</text>
<text x="770" y="513" text-anchor="middle" font-family="system-ui, -apple-system, sans-serif" font-size="11" fill="#CBD5E1">API</text>
<text x="770" y="554" text-anchor="middle" font-family="system-ui, -apple-system, sans-serif" font-size="11" font-weight="500" fill="#9CA3AF">standalone repo</text>
<!-- ============================================ -->
<!-- Cross-repo dependency arrows -->
<!-- ============================================ -->
<!-- App 2 → Lib D (shared): curve down-left -->
<path d="M 675 185 C 620 240, 560 280, 525 325" stroke="#476088" stroke-width="2.5" fill="none" marker-end="url(#arrowBlue)"/>
<!-- Lib A → Lib D: gentle arc down -->
<path d="M 300 155 C 370 200, 420 260, 458 322" stroke="#70AF40" stroke-width="2" fill="none" marker-end="url(#arrowGreen)"/>
<!-- App 3 → Lib B (in Monorepo A): straight up -->
<path d="M 210 458 C 220 380, 230 320, 242 252" stroke="#476088" stroke-width="2.5" fill="none" marker-end="url(#arrowBlue)"/>
<!-- App 3 → Lib E (design): horizontal -->
<path d="M 242 500 C 320 490, 400 500, 458 515" stroke="#476088" stroke-width="2.5" fill="none" marker-end="url(#arrowBlue)"/>
<!-- App 4 → Lib C (in Monorepo B): straight up -->
<path d="M 790 458 C 805 380, 820 310, 835 217" stroke="#476088" stroke-width="2.5" fill="none" marker-end="url(#arrowBlue)"/>
<!-- App 4 → Lib D (shared): arc up-left -->
<path d="M 730 490 C 650 460, 570 410, 525 358" stroke="#476088" stroke-width="2.5" fill="none" marker-end="url(#arrowBlue)"/>
<!-- App 4 → Lib E (design): horizontal left -->
<path d="M 728 505 C 660 510, 580 515, 522 518" stroke="#476088" stroke-width="2" fill="none" marker-end="url(#arrowBlue)"/>
<!-- Lib D → Lib C (cross-repo lib dependency): arc up-right -->
<path d="M 525 330 C 620 300, 740 260, 812 200" stroke="#70AF40" stroke-width="2" fill="none" marker-end="url(#arrowGreen)"/>
</svg>

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 155 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 409 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 188 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 188 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 297 KiB

@@ -2,70 +2,119 @@
import { Icon } from '@astrojs/starlight/components';
import { getCollection } from 'astro:content';
const { pathname } = Astro.url;
const cleanedPath = pathname.split('?')[0];
const allDocs = await getCollection('docs');
const pathSegments = cleanedPath.split('/').filter(Boolean);
type Crumb = {
id: string;
name: string;
href: string;
label: string;
href?: string;
current: boolean;
};
function slugify(label: string): string {
return label
.replace(/\.NET/g, 'dotnet')
.toLowerCase()
.replace(/[^a-z0-9_]+/g, '-')
.replace(/^-+|-+$/g, '');
}
function findDocByPath (path: string) {
path = path.replace(/^docs\//, '');
return allDocs.find(d => d.id === `${path}`)
?? allDocs.find(d => d.id === `${path}/index`);
// --- Primary: Sidebar-based breadcrumbs ---
const sidebar = Astro.locals.starlightRoute.sidebar;
function findBreadcrumbPath(
entries: typeof sidebar,
path: Crumb[] = [],
slugSegments: string[] = []
): Crumb[] | null {
for (const entry of entries) {
if (entry.type === 'link' && entry.isCurrent) {
return [...path, { label: entry.label, href: entry.href, current: true }];
}
if (entry.type === 'group') {
const currentSlugs = [...slugSegments, slugify(entry.label)];
const groupHref = `/docs/${currentSlugs.join('/')}`;
const result = findBreadcrumbPath(
entry.entries,
[
...path,
{ label: entry.label, href: groupHref, current: false },
],
currentSlugs
);
if (result) return result;
}
}
return null;
}
function createNameFromSegment(segment: string): string {
let crumbs: Crumb[] = findBreadcrumbPath(sidebar) ?? [];
// --- Fallback: URL-path-based breadcrumbs ---
if (crumbs.length === 0) {
const { pathname } = Astro.url;
const cleanedPath = pathname.split('?')[0];
const allDocs = await getCollection('docs');
const pathSegments = cleanedPath.split('/').filter(Boolean);
function findDocByPath(path: string) {
path = path.replace(/^docs\//, '');
return (
allDocs.find((d) => d.id === `${path}`) ??
allDocs.find((d) => d.id === `${path}/index`)
);
}
function createNameFromSegment(segment: string): string {
segment = segment.split('#')[0];
return segment
.split('-')
.map(s => s.charAt(0).toUpperCase() + s.slice(1))
.join(' ');
.split('-')
.map((s) => s.charAt(0).toUpperCase() + s.slice(1))
.join(' ');
}
crumbs = pathSegments
.map((segment, index) => {
const docPath = pathSegments.slice(0, index + 1).join('/');
const doc = findDocByPath(docPath);
const label =
doc?.data?.sidebar?.label ||
doc?.data?.title ||
createNameFromSegment(segment);
// If `base` is set in config, don't include it in breadcrumbs
if (index === 0 && import.meta.env.BASE_URL) return null;
return {
label,
href: `/${docPath}`,
current: index === pathSegments.length - 1,
};
})
.filter(Boolean) as Crumb[];
}
const crumbs: Crumb[] = pathSegments.map((segment, index) => {
const docPath = pathSegments.slice(0, index + 1).join('/');
const doc = findDocByPath(docPath);
const name = doc?.data?.sidebar?.label || doc?.data?.title || createNameFromSegment(segment);
// If `base` is set in config, don't include it in breadcrumbs
if (index === 0 && import.meta.env.BASE_URL) return null;
return {
id: segment,
name,
href: `/${docPath}`,
current: index === pathSegments.length - 1,
};
}).filter(Boolean);
---
<nav class="not-content flex mb-4" aria-label="Breadcrumb">
<ol role="list" class="flex m-0 p-0 flex-wrap items-center space-x-2 text-sm">
{crumbs.map((crumb, index) => (
<li class="flex items-center">
{index > 0 && (
<Icon name="right-caret" class="w-5 h-5 ml-2 mr-2"/>
)}
<a
href={crumb.href}
class={`no-underline text-sm${
crumb.current
? ' text-slate-900 dark:text-slate-100 font-semibold'
: ' text-slate-500 dark:text-slate-400 font-medium hover:text-slate-700 dark:hover:text-slate-200'
} transition-colors`}
aria-current={crumb.current ? 'page' : undefined}
>
{crumb.name}
</a>
</li>
))}
{
crumbs.map((crumb, index) => (
<li class="flex items-center">
{index > 0 && <Icon name="right-caret" class="w-5 h-5 ml-2 mr-2" />}
{crumb.href ? (
<a
href={crumb.href}
class={`no-underline text-sm${
crumb.current
? ' text-slate-900 dark:text-slate-100 font-semibold'
: ' text-slate-500 dark:text-slate-400 font-medium hover:text-slate-700 dark:hover:text-slate-200'
} transition-colors`}
aria-current={crumb.current ? 'page' : undefined}
>
{crumb.label}
</a>
) : (
<span class="text-sm text-slate-500 dark:text-slate-400 font-medium">
{crumb.label}
</span>
)}
</li>
))
}
</ol>
</nav>
+16 -15
View File
@@ -447,13 +447,6 @@ const currentVersion = versions.find((v) => v.current);
</div>
<div class="h-6 w-px bg-slate-200 mx-1 dark:bg-slate-700"></div>
<a
id="header-ai-link"
href={`${nxDevUrl}/ai`}
class="px-3 py-2 text-sm font-medium text-slate-600 hover:text-blue-500 rounded-md transition-colors whitespace-nowrap no-underline dark:text-slate-200 dark:hover:text-sky-500"
>
AI
</a>
<a
id="header-nx-cloud-link"
href={`${nxDevUrl}/nx-cloud`}
@@ -516,7 +509,7 @@ const currentVersion = versions.find((v) => v.current);
</div>
<script>
import { sendCustomEvent } from '@nx/nx-dev-feature-analytics';
import { sendCustomEventViaGtm } from '@nx/nx-dev-feature-analytics';
function setupAnalyticsTracking() {
const docsHomeLink = document.getElementById('header-docs-home-link');
@@ -528,7 +521,7 @@ const currentVersion = versions.find((v) => v.current);
const tryNxCloudBtn = document.getElementById('header-try-nx-cloud-btn');
docsHomeLink?.addEventListener('click', () => {
sendCustomEvent(
sendCustomEventViaGtm(
'documentation-click',
'header-navigation',
'documentation-header'
@@ -536,7 +529,7 @@ const currentVersion = versions.find((v) => v.current);
});
aiLink?.addEventListener('click', () => {
sendCustomEvent(
sendCustomEventViaGtm(
'ai-click',
'header-navigation',
'documentation-header'
@@ -544,7 +537,7 @@ const currentVersion = versions.find((v) => v.current);
});
nxCloudLink?.addEventListener('click', () => {
sendCustomEvent(
sendCustomEventViaGtm(
'nx-cloud-click',
'header-navigation',
'documentation-header'
@@ -552,7 +545,7 @@ const currentVersion = versions.find((v) => v.current);
});
pricingLink?.addEventListener('click', () => {
sendCustomEvent(
sendCustomEventViaGtm(
'pricing-click',
'header-navigation',
'documentation-header'
@@ -560,7 +553,7 @@ const currentVersion = versions.find((v) => v.current);
});
enterpriseLink?.addEventListener('click', () => {
sendCustomEvent(
sendCustomEventViaGtm(
'enterprise-click',
'header-navigation',
'documentation-header'
@@ -568,11 +561,19 @@ const currentVersion = versions.find((v) => v.current);
});
contactBtn?.addEventListener('click', () => {
sendCustomEvent('contact-click', 'header-cta', 'documentation-header');
sendCustomEventViaGtm(
'contact-click',
'header-cta',
'documentation-header'
);
});
tryNxCloudBtn?.addEventListener('click', () => {
sendCustomEvent('login-click', 'header-cta', 'documentation-header');
sendCustomEventViaGtm(
'login-click',
'header-cta',
'documentation-header'
);
});
}
@@ -38,7 +38,7 @@ const bannerId = bannerConfig ? `${bannerConfig.title}-${bannerConfig.activeUnti
<GitHubStarWidget starsCount={githubStarsCount} client:load />
<div class="flex flex-col mx-2 gap-2">
<a
href="https://nx.dev/contact"
href={`${import.meta.env.SITE || 'https://nx.dev'}/contact`}
class="w-full inline-flex items-center justify-center px-4 py-2 text-sm font-medium rounded-md transition no-underline border border-slate-300 dark:border-slate-700 bg-white dark:bg-slate-800 text-slate-700 dark:text-slate-200 hover:bg-slate-50 dark:hover:bg-slate-700 shadow-sm"
title="Contact Us"
>
@@ -111,7 +111,7 @@ const bannerId = bannerConfig ? `${bannerConfig.title}-${bannerConfig.activeUnti
inset-block: var(--sl-nav-height) 0;
inset-inline-start: 0;
width: 100%;
background-color: var(--sl-color-black);
background-color: var(--sl-color-bg-sidebar);
overflow-y: auto;
}
+68 -7
View File
@@ -2,24 +2,81 @@
import MobileMenuFooter from '@astrojs/starlight/components/MobileMenuFooter.astro'
import SidebarPersister from '@astrojs/starlight/components/SidebarPersister.astro'
import SidebarSublist from './SidebarSublist.astro'
import { GitHubStarWidget } from '@nx/nx-dev-ui-common/src/lib/github-star-widget';
import TabbedSidebar from './sidebar-tabs/TabbedSidebar.astro'
import SidebarTab from './sidebar-tabs/SidebarTab.astro'
import SidebarTabPanel from './sidebar-tabs/SidebarTabPanel.astro'
import type { SidebarEntry } from './SidebarSublist.astro'
import { sidebarTabs } from '../../../sidebar.mts'
const { sidebar } = Astro.locals.starlightRoute
const githubStarsCount = Astro.locals.githubStarsCount ?? 0;
// Check if any entry in a tree has isCurrent
function hasCurrentPage(entries: SidebarEntry[]): boolean {
return entries.some((entry) => {
if (entry.type === 'link') return entry.isCurrent
return hasCurrentPage(entry.entries)
})
}
// For each tab, resolve its groups from the sidebar using the tab's group labels
const tabs = sidebarTabs.map((config) => {
const groupLabels = config.groups.map((g) =>
typeof g === 'string' ? g : g.label
)
const groups = sidebar.filter(
(entry: SidebarEntry) => entry.type === 'group' && groupLabels.includes(entry.label)
)
// Single-group tabs: unwrap the top-level group to avoid redundant heading
const isSingleGroup = groups.length === 1 && groups[0].type === 'group'
const entries: SidebarEntry[] = isSingleGroup ? (groups[0] as any).entries : groups
const active = hasCurrentPage(groups)
return { ...config, entries, active }
})
// Exactly one tab should be active; if none matched, leave it for the client to decide
const anyActive = tabs.some((t) => t.active)
---
<div class="sidebar-wrapper" data-testid="sidebar-wrapper">
<SidebarPersister>
<SidebarSublist sublist={sidebar}/>
<TabbedSidebar>
{tabs.map((tab) => (
<SidebarTab
slot="tabs"
id={tab.id}
icon={tab.icon}
label={tab.label}
active={anyActive ? tab.active : false}
/>
))}
{tabs.map((tab) => (
<SidebarTabPanel
slot="panels"
id={`${tab.id}-panel`}
tabId={tab.id}
active={anyActive ? tab.active : false}
>
<SidebarSublist sublist={tab.entries} />
</SidebarTabPanel>
))}
</TabbedSidebar>
</SidebarPersister>
<div class="md:sl-hidden">
<MobileMenuFooter/>
</div>
</div>
<script>
// Enable sidebar expand/collapse animations after first paint so that
// groups that are already open on page load don't animate from closed.
requestAnimationFrame(() => {
document.querySelector('.sidebar-wrapper')?.classList.add('sidebar-animate');
});
</script>
<style>
.sidebar-wrapper :global(a) {
color: var(--sl-color-gray-4);
color: var(--sl-color-gray-3);
}
.sidebar-wrapper :global(a[aria-current=page]) {
@@ -35,11 +92,15 @@ const githubStarsCount = Astro.locals.githubStarsCount ?? 0;
}
.sidebar-wrapper :global(details summary .group-label) {
font-size: var(--text-lg);
font-weight: var(--font-weight-semibold);
font-size: var(--text-base);
font-weight: var(--font-weight-medium);
}
.sidebar-wrapper :global(ul ul summary) {
font-weight: var(--font-weight-medium);
}
.sidebar-wrapper :global(details[open] summary .group-label) {
color: var(--sl-color-gray-2);
color: var(--sl-color-text-accent);
}
</style>
@@ -3,43 +3,43 @@
* This is a modified version of the SidebarSublist component from Starlight with support for icons.
* https://github.com/withastro/starlight/blob/46524ac/packages/starlight/components/SidebarSublist.astro -->
*/
import { Badge, Icon } from '@astrojs/starlight/components'
import { Badge, Icon } from '@astrojs/starlight/components';
export interface SidebarLink {
type: 'link'
label: string
href: string
isCurrent: boolean
badge: any
attrs: any
type: 'link';
label: string;
href: string;
isCurrent: boolean;
badge: any;
attrs: any;
}
export interface SidebarGroup {
type: 'group'
label: string
entries: (SidebarLink | SidebarGroup)[]
collapsed: boolean
badge: any
attrs?: any
type: 'group';
label: string;
entries: (SidebarLink | SidebarGroup)[];
collapsed: boolean;
badge: any;
attrs?: any;
}
export type SidebarEntry = SidebarLink | SidebarGroup
export type SidebarEntry = SidebarLink | SidebarGroup;
interface Props {
sublist: SidebarEntry[]
sublist: SidebarEntry[];
nested?: boolean
nested?: boolean;
}
const { sublist, nested } = Astro.props
const { sublist, nested } = Astro.props;
// Copied from https://github.com/withastro/starlight/blob/46524ac/packages/starlight/utils/navigation.ts#L447
function flattenSidebar(entries: SidebarEntry[]): SidebarEntry[] {
return entries.reduce<SidebarEntry[]>((acc, entry) => {
if (entry.type === 'group') acc.push(...flattenSidebar(entry.entries))
else acc.push(entry)
return acc
}, [])
if (entry.type === 'group') acc.push(...flattenSidebar(entry.entries));
else acc.push(entry);
return acc;
}, []);
}
function getIconPath(iconName: string | undefined): string | null {
@@ -53,7 +53,7 @@ function getIconPath(iconName: string | undefined): string | null {
sublist.map((entry) => {
const icon = getIconPath(entry.attrs?.['data-icon']);
return (
<li class={icon ? 'p-0 border-none' : ''}>
<li class={icon ? 'border-none p-0' : ''}>
{entry.type === 'link' ? (
<a
href={entry.href}
@@ -66,35 +66,47 @@ function getIconPath(iconName: string | undefined): string | null {
aria-hidden="true"
src={icon}
alt=""
class="sidebar-icon w-4 h-4 dark:invert"
class="sidebar-icon h-4 w-4 dark:invert"
/>
)}
<span>{entry.label}</span>
{entry.badge &&
<Badge variant={entry.badge.variant} class={entry.badge.class}
text={entry.badge.text}/>}
{entry.badge && (
<Badge
variant={entry.badge.variant}
class={entry.badge.class}
text={entry.badge.text}
/>
)}
</a>
) : (
<details
open={flattenSidebar(entry.entries).some((i: any) => i.isCurrent) || !entry.collapsed}>
<summary class={icon ? 'pl-0 py-2' : ''}>
open={
flattenSidebar(entry.entries).some((i: any) => i.isCurrent) ||
!entry.collapsed
}
>
<summary class={icon ? 'py-2 pl-0' : ''}>
<div class="group-label">
{icon && (
<img
aria-hidden="true"
src={icon}
alt=""
class="sidebar-icon w-4 h-4 dark:invert"
class="sidebar-icon h-4 w-4 dark:invert"
/>
)}
<span class="large">{entry.label}</span>
{entry.badge && (
<Badge variant={entry.badge.variant} class={entry.badge.class} text={entry.badge.text}/>
<Badge
variant={entry.badge.variant}
class={entry.badge.class}
text={entry.badge.text}
/>
)}
</div>
<Icon name="right-caret" class="caret" size="1.25rem"/>
<Icon name="right-caret" class="caret" size="1.25rem" />
</summary>
<Astro.self sublist={entry.entries} nested/>
<Astro.self sublist={entry.entries} nested />
</details>
)}
</li>
@@ -107,7 +119,7 @@ function getIconPath(iconName: string | undefined): string | null {
<style>
@layer starlight.core {
ul {
--sl-sidebar-item-padding-inline: 0.5rem;
--sl-sidebar-item-padding-inline: 0.375rem;
list-style: none;
padding: 0;
}
@@ -128,8 +140,18 @@ function getIconPath(iconName: string | undefined): string | null {
color: var(--sl-color-white);
}
a.large {
font-size: inherit;
font-weight: inherit;
color: inherit;
}
.top-level {
padding-top: 0.75rem;
}
.top-level > li + li {
margin-top: 0.75rem;
margin-top: 0.5rem;
}
summary {
@@ -140,6 +162,17 @@ function getIconPath(iconName: string | undefined): string | null {
line-height: 1.4;
cursor: pointer;
user-select: none;
font-weight: 600;
color: var(--sl-color-white);
border-radius: 0.25rem;
}
summary:hover {
background-color: color-mix(
in srgb,
var(--sl-color-gray-5) 40%,
transparent
);
}
summary::marker,
@@ -148,8 +181,16 @@ function getIconPath(iconName: string | undefined): string | null {
}
.caret {
transition: transform 0.2s ease-in-out;
transition:
transform 0.2s ease-in-out,
color 0.15s ease;
flex-shrink: 0;
color: var(--sl-color-gray-4);
}
.caret:hover,
summary:hover .caret {
color: var(--sl-color-gray-2);
}
:global([dir='rtl']) .caret {
@@ -158,6 +199,11 @@ function getIconPath(iconName: string | undefined): string | null {
[open] > summary .caret {
transform: rotateZ(90deg);
color: var(--sl-color-text-accent);
}
[open] > summary {
color: var(--sl-color-text-accent);
}
a {
@@ -165,7 +211,7 @@ function getIconPath(iconName: string | undefined): string | null {
border-radius: 0.25rem;
text-decoration: none;
color: var(--sl-color-gray-2);
padding: 0.3em var(--sl-sidebar-item-padding-inline);
padding: 0.2em var(--sl-sidebar-item-padding-inline);
line-height: 1.4;
}
@@ -187,9 +233,60 @@ function getIconPath(iconName: string | undefined): string | null {
margin-inline-end: 0.25em;
}
/*
* Deep nesting adjustments (3+ levels).
* Uses :is() to target any li that is at least 3 levels deep,
* then tightens spacing progressively via custom properties.
*/
ul ul ul {
--_nest-indent: 0.25rem;
--_nest-link-block: 0.15em;
}
ul ul ul ul {
--_nest-indent: 0.2rem;
}
ul ul ul li {
margin-inline-start: var(--_nest-indent);
padding-inline-start: var(--_nest-indent);
}
ul ul ul a {
padding-block: var(--_nest-link-block);
}
/* Smooth expand/collapse — progressive enhancement (Chrome 129+, Firefox 131+, Safari 17.5+).
Gated behind .sidebar-animate (added by JS after first paint) to prevent
already-open groups from animating on page load. */
details {
interpolate-size: allow-keywords;
}
:global(.sidebar-animate) details > ul {
overflow: hidden;
height: 0;
opacity: 0;
transition:
height 0.2s ease,
opacity 0.15s ease;
}
:global(.sidebar-animate) details[open] > ul {
height: auto;
opacity: 1;
}
@starting-style {
:global(.sidebar-animate) details[open] > ul {
height: 0;
opacity: 0;
}
}
@media (min-width: 50rem) {
.top-level > li + li {
margin-top: 0.5rem;
margin-top: 0.375rem;
}
.large {
@@ -0,0 +1,25 @@
---
import { Icon } from '@astrojs/starlight/components'
interface Props {
id: string
icon?: string
label: string
active?: boolean
}
const { id, icon, label, active = false } = Astro.props
const panelId = `${id}-panel`
---
<button
role="tab"
id={id}
aria-selected={active ? 'true' : 'false'}
aria-controls={panelId}
data-active={active ? 'true' : undefined}
tabindex={active ? 0 : -1}
>
{icon && <Icon name={icon} size="1rem" />}
{label}
</button>
@@ -0,0 +1,18 @@
---
interface Props {
id: string
tabId: string
active?: boolean
}
const { id, tabId, active = false } = Astro.props
---
<div
role="tabpanel"
id={id}
aria-labelledby={tabId}
hidden={!active}
>
<slot />
</div>
@@ -0,0 +1,117 @@
---
// TabbedSidebar: A custom element that shows/hides tab panels in the sidebar.
// All panels are rendered at build time; JS toggles visibility.
---
<nx-tabbed-sidebar>
<div class="nx-tab-bar-sticky">
<div class="nx-tab-bar" role="tablist" aria-label="Sidebar sections" aria-orientation="vertical">
<slot name="tabs" />
</div>
</div>
<div class="nx-tab-panels">
<slot name="panels" />
</div>
</nx-tabbed-sidebar>
<script>
class NxTabbedSidebar extends HTMLElement {
private tabs: HTMLButtonElement[] = [];
private panels: HTMLElement[] = [];
connectedCallback() {
this.tabs = Array.from(this.querySelectorAll<HTMLButtonElement>('[role="tab"]'));
this.panels = Array.from(this.querySelectorAll<HTMLElement>('[role="tabpanel"]'));
if (this.tabs.length === 0) return;
// Determine initial active tab
const activeIndex = this.getInitialTabIndex();
this.switchTab(activeIndex, false);
// Event listeners
this.addEventListener('click', this.handleClick);
this.addEventListener('keydown', this.handleKeydown);
}
disconnectedCallback() {
this.removeEventListener('click', this.handleClick);
this.removeEventListener('keydown', this.handleKeydown);
}
private getInitialTabIndex(): number {
// 1. Tab with data-active="true" (set server-side from isCurrent check)
const serverActive = this.tabs.findIndex(
(tab) => tab.getAttribute('data-active') === 'true'
);
if (serverActive !== -1) return serverActive;
// 2. sessionStorage fallback
const stored = sessionStorage.getItem('nx-sidebar-tab');
if (stored !== null) {
const storedIndex = this.tabs.findIndex((tab) => tab.id === stored);
if (storedIndex !== -1) return storedIndex;
}
// 3. Default to first tab
return 0;
}
private switchTab(index: number, store = true) {
this.tabs.forEach((tab, i) => {
const selected = i === index;
tab.setAttribute('aria-selected', String(selected));
tab.tabIndex = selected ? 0 : -1;
});
this.panels.forEach((panel, i) => {
panel.hidden = i !== index;
});
if (store && this.tabs[index]) {
sessionStorage.setItem('nx-sidebar-tab', this.tabs[index].id);
}
}
private handleClick = (e: Event) => {
const tab = (e.target as HTMLElement).closest<HTMLButtonElement>('[role="tab"]');
if (!tab) return;
const index = this.tabs.indexOf(tab);
if (index !== -1) {
this.switchTab(index);
tab.focus();
}
};
private handleKeydown = (e: KeyboardEvent) => {
const tab = (e.target as HTMLElement).closest<HTMLButtonElement>('[role="tab"]');
if (!tab) return;
const currentIndex = this.tabs.indexOf(tab);
let nextIndex: number | null = null;
switch (e.key) {
case 'ArrowDown':
nextIndex = (currentIndex + 1) % this.tabs.length;
break;
case 'ArrowUp':
nextIndex = (currentIndex - 1 + this.tabs.length) % this.tabs.length;
break;
case 'Home':
nextIndex = 0;
break;
case 'End':
nextIndex = this.tabs.length - 1;
break;
default:
return;
}
e.preventDefault();
this.switchTab(nextIndex);
this.tabs[nextIndex].focus();
};
}
customElements.define('nx-tabbed-sidebar', NxTabbedSidebar);
</script>
@@ -0,0 +1,108 @@
---
import { getCollection } from 'astro:content';
import Cards from './Cards.astro';
import LinkCard from './LinkCard.astro';
export interface Props {
group: string;
}
const { group } = Astro.props;
const groupSegments = group.split('/');
// Use the Starlight resolved sidebar from Astro locals
const sidebar = Astro.locals.starlightRoute.sidebar;
type SidebarEntry = (typeof sidebar)[number];
function findGroup(
entries: SidebarEntry[],
segments: string[],
depth: number = 0
): SidebarEntry | null {
for (const entry of entries) {
if (entry.type === 'group' && entry.label === segments[depth]) {
if (depth === segments.length - 1) {
return entry;
}
return findGroup(entry.entries, segments, depth + 1);
}
}
return null;
}
interface LinkItem {
label: string;
href: string;
}
function slugify(label: string): string {
return label
.replace(/\.NET/g, 'dotnet')
.toLowerCase()
.replace(/[^a-z0-9_]+/g, '-')
.replace(/^-+|-+$/g, '');
}
// Build base URL path from the group prop segments
const baseSlug = groupSegments.map((s) => slugify(s)).join('/');
function collectTopLevelItems(entries: SidebarEntry[]): LinkItem[] {
const items: LinkItem[] = [];
for (const entry of entries) {
if (entry.type === 'link') {
items.push({ label: entry.label, href: entry.href });
}
if (entry.type === 'group') {
const groupSlug = slugify(entry.label);
items.push({
label: entry.label,
href: `/docs/${baseSlug}/${groupSlug}`,
});
}
}
return items;
}
const matchedGroup = findGroup(sidebar, groupSegments);
const linkItems =
matchedGroup && matchedGroup.type === 'group'
? collectTopLevelItems(matchedGroup.entries)
: [];
// Look up each page in the docs collection for descriptions
const allDocs = await getCollection('docs');
const processedPages = linkItems.map((item) => {
const docPath = item.href.replace(/^\/docs\//, '');
const doc = allDocs.find(
(d) =>
d.id === docPath ||
d.id === `${docPath}.mdoc` ||
d.id === `${docPath}/index` ||
d.id === `${docPath}/index.mdoc`
);
return {
title: item.label,
description: doc?.data?.description || '',
href: item.href,
};
});
---
{
processedPages.length > 0 ? (
<Cards>
{processedPages.map((page) => (
<LinkCard
title={page.title}
description={page.description}
href={page.href}
type="documentation"
/>
))}
</Cards>
) : (
<p>No pages found in this section.</p>
)
}
@@ -548,5 +548,10 @@
"name": "@berenddeboer/nx-biome",
"description": "A self-inferring Nx plugin for using biome to format and lint projects. Supports --batch",
"url": "https://github.com/berenddeboer/nx-plugins/tree/main/packages/nx-biome"
},
{
"name": "@berenddeboer/nx-knip",
"description": "A self-inferring Nx plugin for using knip to find and fix unused dependencies, exports and files",
"url": "https://github.com/berenddeboer/nx-plugins/tree/main/packages/nx-knip"
}
]
@@ -1,284 +0,0 @@
---
title: Common Task Configurations
description: Learn about standard task naming conventions in Nx projects, including build, serve, test, and lint tasks, for consistent project configuration.
keywords: [build, serve, test, lint]
sidebar:
order: 1
label: Common Tasks
filter: 'type:Concepts'
weight: 5.0
---
The tasks that are [inferred by plugins](/docs/concepts/inferred-tasks) or that you define in your [project configuration](/docs/reference/project-configuration) can have any name that you want, but it is helpful for developers if you keep your task naming convention consistent across the projects in your repository. This way, if a developer moves from one project to another, they already know how to launch tasks for the new project. Here are some common task names that you can define for your projects.
## `build`
This task should produce the compiled output of this project. Typically, you'll want to have `build` tasks depend on the `build` tasks of project dependencies across the whole repository. You can set this default in the `nx.json` file like this:
```json title="nx.json"
{
"targetDefaults": {
"build": {
"dependsOn": ["^build"]
}
}
}
```
The task might use the [@nx/vite](/docs/technologies/build-tools/vite/introduction), [@nx/webpack](/docs/technologies/build-tools/webpack/introduction) or [@nx/rspack](/docs/technologies/build-tools/rspack/introduction) plugins. Or you could have the task launch your own custom script.
{% tabs %}
{% tabitem label="Vite" %}
Set up an [inferred](/docs/concepts/inferred-tasks) `build` task for every project that has a Vite configuration file with this configuration in `nx.json`:
```json title="nx.json"
{
"plugins": [
{
"plugin": "@nx/vite/plugin",
"options": {
"buildTargetName": "build"
}
}
]
}
```
You can also [override the inferred task configuration](/docs/concepts/inferred-tasks#overriding-inferred-task-configuration) as needed.
{% /tabitem %}
{% tabitem label="Webpack" %}
Set up an [inferred](/docs/concepts/inferred-tasks) `build` task for every project that has a Webpack configuration file with this configuration in `nx.json`:
```json title="nx.json"
{
"plugins": [
{
"plugin": "@nx/webpack/plugin",
"options": {
"buildTargetName": "build"
}
}
]
}
```
You can also [override the inferred task configuration](/docs/concepts/inferred-tasks#overriding-inferred-task-configuration) as needed.
{% /tabitem %}
{% tabitem label="rspack" %}
Set up an [inferred](/docs/concepts/inferred-tasks) `build` task for every project that has an rspack configuration file with this configuration in `nx.json`:
```json title="nx.json"
{
"plugins": [
{
"plugin": "@nx/rspack/plugin",
"options": {
"buildTargetName": "build"
}
}
]
}
```
You can also [override the inferred task configuration](/docs/concepts/inferred-tasks#overriding-inferred-task-configuration) as needed.
{% /tabitem %}
{% tabitem label="Custom Script" %}
You can define your own `build` task in your project configuration. Here is an example that uses `ts-node` to run a node script.
```json title="packages/my-project/package.json"
{
"scripts": {
"build": "ts-node build-script.ts"
}
}
```
{% /tabitem %}
{% /tabs %}
## `serve`
This task should run your project in a developer preview mode. The task might use the [@nx/vite](/docs/technologies/build-tools/vite/introduction), [@nx/webpack](/docs/technologies/build-tools/webpack/introduction) or [@nx/rspack](/docs/technologies/build-tools/rspack/introduction) plugins. Or you could have the task launch your own custom script.
{% tabs %}
{% tabitem label="Vite" %}
Set up an [inferred](/docs/concepts/inferred-tasks) `serve` task for every project that has a Vite configuration file with this configuration in `nx.json`:
```json title="nx.json"
{
"plugins": [
{
"plugin": "@nx/vite/plugin",
"options": {
"serveTargetName": "serve"
}
}
]
}
```
You can also [override the inferred task configuration](/docs/concepts/inferred-tasks#overriding-inferred-task-configuration) as needed.
{% /tabitem %}
{% tabitem label="Webpack" %}
Set up an [inferred](/docs/concepts/inferred-tasks) `serve` task for every project that has a Webpack configuration file with this configuration in `nx.json`:
```json title="nx.json"
{
"plugins": [
{
"plugin": "@nx/webpack/plugin",
"options": {
"serveTargetName": "serve"
}
}
]
}
```
You can also [override the inferred task configuration](/docs/concepts/inferred-tasks#overriding-inferred-task-configuration) as needed.
{% /tabitem %}
{% tabitem label="rspack" %}
Set up an [inferred](/docs/concepts/inferred-tasks) `serve` task for every project that has an rspack configuration file with this configuration in `nx.json`:
```json title="nx.json"
{
"plugins": [
{
"plugin": "@nx/rspack/plugin",
"options": {
"serveTargetName": "serve"
}
}
]
}
```
You can also [override the inferred task configuration](/docs/concepts/inferred-tasks#overriding-inferred-task-configuration) as needed.
{% /tabitem %}
{% tabitem label="Custom Script" %}
You can define your own `serve` task in your project configuration. Here is an example that uses `ts-node` to run the entry point of your project.
```json title="packages/my-project/package.json"
{
"scripts": {
"serve": "ts-node main.ts"
}
}
```
{% /tabitem %}
{% /tabs %}
## `test`
This task typically runs unit tests for a project. The task might use the [@nx/vite](/docs/technologies/build-tools/vite/introduction) or [@nx/jest](/docs/technologies/test-tools/jest/introduction) plugins. Or you could have the task launch your own custom script.
{% tabs %}
{% tabitem label="Vitest" %}
Set up an [inferred](/docs/concepts/inferred-tasks) `test` task for every project that has a Vitest configuration file with this configuration in `nx.json`:
```json title="nx.json"
{
"plugins": [
{
"plugin": "@nx/vite/plugin",
"options": {
"testTargetName": "test"
}
}
]
}
```
You can also [override the inferred task configuration](/docs/concepts/inferred-tasks#overriding-inferred-task-configuration) as needed.
{% /tabitem %}
{% tabitem label="Jest" %}
Set up an [inferred](/docs/concepts/inferred-tasks) `test` task for every project that has a Jest configuration file with this configuration in `nx.json`:
```json title="nx.json"
{
"plugins": [
{
"plugin": "@nx/jest/plugin",
"options": {
"targetName": "test"
}
}
]
}
```
You can also [override the inferred task configuration](/docs/concepts/inferred-tasks#overriding-inferred-task-configuration) as needed.
{% /tabitem %}
{% tabitem label="Custom Script" %}
You can define your own `test` task in your project configuration. Here is an example that runs the `ava` test tool.
```json title="packages/my-project/package.json"
{
"scripts": {
"test": "ava"
}
}
```
{% /tabitem %}
{% /tabs %}
## `lint`
This task should run lint rules for a project. The task might use the [@nx/eslint](/docs/technologies/eslint/introduction) plugin or run your own custom script.
{% tabs %}
{% tabitem label="ESLint" %}
Set up an [inferred](/docs/concepts/inferred-tasks) `lint` task for every project that has an ESLint configuration file with this configuration in `nx.json`:
```json title="nx.json"
{
"plugins": [
{
"plugin": "@nx/eslint/plugin",
"options": {
"targetName": "lint"
}
}
]
}
```
You can also [override the inferred task configuration](/docs/concepts/inferred-tasks#overriding-inferred-task-configuration) as needed.
{% /tabitem %}
{% tabitem label="Custom Script" %}
You can define your own `lint` task in your project configuration. Here is an example that runs the `sonarts` lint tool.
```json title="packages/my-project/package.json"
{
"scripts": {
"lint": "sonarts"
}
}
```
{% /tabitem %}
{% /tabs %}
@@ -24,7 +24,7 @@ By default, the computation hash for something like `nx test remixapp` includes:
After Nx computes the hash for a task, it then checks if it ran this exact computation before. First, it checks locally, and then if it is missing, and if a remote cache is configured, it checks remotely. If a matching computation is found, Nx retrieves and replays it. This includes restoring files.
Nx places the right files in the right folders and prints the terminal output. From the user's point of view, the command ran the same, just a lot faster.
Nx places the right files in the right folders and prints the terminal output. From the user's point of view, the command ran the same, only a lot faster.
![cache](../../../assets/concepts/caching/cache.svg)
@@ -32,7 +32,7 @@ If Nx doesn't find a corresponding computation hash, Nx runs the task, and after
## Optimizations
Although conceptually this is fairly straightforward, Nx optimizes the experience for you. For instance, Nx:
Nx optimizes the caching experience in several ways. For instance, Nx:
- Captures stdout and stderr to make sure the replayed output looks the same, including on Windows.
- Minimizes the IO by remembering what files are replayed where.
@@ -46,7 +46,7 @@ As your workspace grows, the task graph looks more like this:
All of these optimizations are crucial for making Nx usable for any non-trivial workspace. Only the minimum amount of
work happens. The rest is either left as is or restored from the cache.
## Fine-tuning Nx's Cache
## Fine-tuning the Nx cache
Each cacheable task defines a set of inputs and outputs. Inputs are factors Nx considers when calculating the computation hash.
Outputs are files that will be cached and restored when the computation hash matches.
@@ -6,9 +6,7 @@ sidebar:
filter: 'type:Concepts'
---
Nx is a VSCode of build tools, with a powerful core, driven by metadata, and extensible through [plugins](/docs/concepts/nx-plugins). Nx works with a
few concepts to drive your monorepo efficiently, and effectively. This guide covers the mental model around how Nx works
with project graphs, task graphs, affected commands, computation hashing and caching.
Nx is a VSCode of build tools, with a powerful core, driven by metadata, and extensible through [plugins](/docs/concepts/nx-plugins). Nx works with a few core concepts to drive your monorepo efficiently: project graphs, task graphs, affected commands, computation hashing, and caching.
## The project graph
@@ -267,7 +265,7 @@ With this, running the same test command creates the following task graph:
This often makes more sense for builds, where to build `app1`, you want to build `lib` first. You can also define
similar
relationships between targets of the same project, including a test target that depends on the build.
relationships between targets of the same project, including a test target that depends on the build. Learn more about configuring task pipelines in [Task Pipeline Configuration](/docs/concepts/task-pipeline-configuration).
A task graph can contain different targets, and those can run in parallel. For instance, as Nx is building `app2`, it
can be testing `app1` at the same time.
@@ -287,7 +285,7 @@ and `lib:test`.
When you run `nx run-many -t test`, you are telling Nx to do this for all the projects.
As your workspace grows, retesting all projects becomes too slow. To address this Nx implements code change analysis to
As your workspace grows, retesting all projects becomes too slow. To address this Nx implements code change analysis via the [`affected` command](/docs/features/ci-features/affected) to
get the min set of projects that need to be retested. How does it work?
When you run `nx affected -t test`, Nx looks at the files you changed in your PR, it will look at the nature of
@@ -304,104 +302,19 @@ that `app2` cannot be affected by it, so it only retests `app1`.
## Computation hashing and caching
Nx runs the tasks in the task graph in the right order. Before running the task, Nx computes its computation hash. As
long as the computation hash is the same, the output of running the task is the same.
How does Nx do it?
By default, the computation hash for say `nx test app1` includes:
- All the source files of `app1` and `lib`
- Relevant global configuration
- Versions of external dependencies
- [Runtime values provisioned by the user](/docs/reference/inputs#runtime-inputs)
- CLI Command flags
Before running a task, Nx computes a hash based on source files, configuration, dependencies, and other inputs. If the hash matches a previous run, the cached result is replayed — including terminal output and file artifacts. If not, Nx runs the task and stores the result for next time.
![computation-hashing](../../../assets/concepts/mental-model/computation-hashing.svg)
This behavior is customizable. For instance, lint checks may only depend on the source code of the project and global
configs. Builds can depend on the `.d.ts` files of the compiled libs instead of their source.
After Nx computes the hash for a task, it then checks if it ran this exact computation before. First, it checks locally,
and then if it is missing, and if a remote cache is configured, it checks remotely.
If Nx finds the computation, Nx retrieves it and replays it. Nx places the right files in the right folders and prints
the terminal output. So from the user's point of view, the command ran the same, just a lot faster.
Nx checks the local cache first, then the [remote cache](/docs/features/ci-features/remote-cache) if configured. From the user's point of view, the command ran the same, only a lot faster.
![cache](../../../assets/concepts/mental-model/cache.svg)
If Nx doesn't find this computation, Nx runs the task, and after it completes, it takes the outputs and the terminal
output and stores it locally (and if configured remotely). All of this happens transparently, so you don't have to worry
about it.
Although conceptually this is fairly straightforward, Nx optimizes this to make this experience good for you. For
instance, Nx:
- Captures stdout and stderr to make sure the replayed output looks the same, including on Windows.
- Minimizes the IO by remembering what files are replayed where.
- Only shows relevant output when processing a large task graph.
- Provides affordances for troubleshooting cache misses. And many other optimizations.
As your workspace grows, the task graph looks more like this:
{% graph height="200px" type="task"%}
```json
{
"projects": [
{
"name": "lib",
"type": "lib",
"data": {
"tags": [],
"targets": {
"test": {}
}
}
}
],
"taskIds": ["lib:test"],
"taskGraph": {
"roots": ["lib:test"],
"tasks": {
"lib:test": {
"id": "lib:test",
"target": {
"project": "lib",
"target": "test"
},
"projectRoot": "libs/lib",
"overrides": {}
}
},
"dependencies": {}
}
}
```
{% /graph %}
All of these optimizations are crucial for making Nx usable for any non-trivial workspace. Only the minimum amount of
work happens. The rest is either left as is or restored from the cache.
See [How Caching Works](/docs/concepts/how-caching-works) for the complete list of hash inputs, cache configuration options, and optimization details.
## Distributed task execution
Nx supports running commands across multiple machines. You can either set it up by hand or use Nx Cloud. [Read the comparison of the two approaches.](https://nx.dev/blog/distributing-ci-binning-and-distributed-task-execution)
When using the distributed task execution, Nx is able to run any task graph on many agents instead of locally.
For instance, `nx affected --build` won't run the build locally (which can take hours for large workspaces). Instead,
it will send the Task Graph to Nx Cloud. Nx Cloud Agents will then pick up the tasks they can run and execute them.
Note that this happens transparently. If an agent builds `app1`, it will fetch the outputs for `lib` if it doesn't have
them
already.
As agents complete tasks, the main job where you invoked `nx affected --build` will start receiving created files and
terminal outputs.
After `nx affected --build` completes, the machine will have the build files and all the terminal outputs as if it ran
it locally.
For large workspaces, even with caching, running all tasks on a single machine can be slow. [Nx Agents](/docs/features/ci-features/distribute-task-execution) can distribute the task graph across multiple machines, running tasks in parallel while using [remote caching](/docs/features/ci-features/remote-cache) to share artifacts between agents. From your CI's perspective, the results appear as if everything ran on a single machine.
![Distribution](../../../assets/concepts/mental-model/dte.svg)
@@ -410,5 +323,5 @@ it locally.
- Nx is able to analyze your source code to create a Project Graph.
- Nx can use the project graph and information about projects' targets to create a Task Graph.
- Nx is able to perform code-change analysis to create the smallest task graph for your PR.
- Nx supports computation caching to never execute the same computation twice. This computation cache is pluggable and
- Nx supports [computation caching](/docs/features/cache-task-results) to never execute the same computation twice. This computation cache is pluggable and
can be distributed.
@@ -49,7 +49,7 @@ To see information about the running Nx Daemon (such as its background process I
## Customizing the socket location
The Nx Daemon uses a unix socket to communicate between the daemon and the Nx processes. By default this socket gets placed in a temp directory. If you are using Nx in a docker-compose environment, however, you may want to run the daemon manually
and control its location to enable sharing the daemon among your docker containers. To do so, simply set the NX_DAEMON_SOCKET_DIR environment variable to a shared directory.
and control its location to enable sharing the daemon among your docker containers. To do so, set the `NX_DAEMON_SOCKET_DIR` environment variable to a shared directory.
## Daemon Behavior in Containers
@@ -13,7 +13,7 @@ For example, plugins can accomplish the following:
- [Configure Nx cache settings](/docs/concepts/inferred-tasks) for a tool. The [`@nx/webpack`](/docs/technologies/build-tools/webpack/introduction) plugin can automatically configure the [inputs](/docs/guides/tasks--caching/configure-inputs) and [outputs](/docs/guides/tasks--caching/configure-outputs) for a `build` task based on the settings in the `webpack.config.js` file it uses.
- [Update tooling configuration](/docs/features/automate-updating-dependencies) when upgrading the tool version. When Storybook 7 introduced a [new format](https://storybook.js.org/blog/storybook-csf3-is-here) for their configuration files, anyone using the [`@nx/storybook`](/docs/technologies/test-tools/storybook/introduction) plugin could automatically apply those changes to their repository when upgrading.
- [Set up a tool](/docs/features/generate-code) for the first time. With the [`@nx/playwright`](/docs/technologies/test-tools/playwright/introduction) plugin installed, you can use the `@nx/playwright:configuration` code generator to set up Playwright tests in an existing project.
- [Run a tool in an advanced way](/docs/concepts/executors-and-configurations). The [`@nx/js`](/docs/technologies/typescript/introduction) plugin's [`@nx/js:tsc` executor](/docs/technologies/typescript/executors#tsc) combines Nx's understanding of your repository with Typescript's native batch mode feature to make your builds [even more performant](/docs/technologies/typescript/guides/enable-tsc-batch-mode).
- [Run a tool in an advanced way](/docs/concepts/executors-and-configurations). The [`@nx/js`](/docs/technologies/typescript/introduction) plugin's [`@nx/js:tsc` executor](/docs/technologies/typescript/executors#tsc) combines the Nx understanding of your repository with Typescript's native batch mode feature to make your builds [even more performant](/docs/technologies/typescript/guides/enable-tsc-batch-mode).
## Plugin Features
@@ -0,0 +1,48 @@
---
title: Synthetic Monorepos
description: Learn how synthetic monorepos connect separate repositories into a unified dependency graph, giving you monorepo intelligence without moving code.
---
Most organizations don't have a single giant monorepo. They have a handful of monorepos per team or domain, plus dozens of standalone repos. Consolidating everything into one repository is not just a technical challenge. The organizational side (bringing teams along, changing workflows, ensuring adoption) is often harder than the code migration itself.
Synthetic monorepos let you get monorepo benefits without that consolidation.
## What is a synthetic monorepo?
A synthetic monorepo connects separate repositories into a unified dependency graph without moving any code. Which repo depends on which, what a change affects downstream, how projects relate across teams: all of that becomes visible automatically.
![A synthetic monorepo connecting multiple monorepos and standalone repos into a unified dependency graph](../../../assets/concepts/synthetic-monorepo.svg)
Unlike a traditional monorepo where all code lives in one repository, a synthetic monorepo leaves each repository where it is. Instead, it builds a cross-repo graph that tooling can reason about, just as if the code were in one place.
## What synthetic monorepos enable
A synthetic monorepo addresses several downsides of a polyrepo setup:
**Visibility** — An automatic cross-repo dependency graph shows which repo depends on which and what a change affects downstream. Always up to date, discovered from actual code — not a manually maintained spreadsheet or catalog. Nx implements this through the [Workspace Graph](/docs/enterprise/polygraph).
**Coordination** — Cross-repo changes no longer require manually sequencing PRs, managing compatibility, and coordinating release order. Tooling on top of the graph enables impact analysis, coordinated changes, and conformance checking across repo boundaries.
**Governance** — Organizational standards apply across every connected repo through [conformance rules](/docs/enterprise/conformance). Scheduled [custom workflows](/docs/enterprise/custom-workflows) check repos continuously — even ones nobody has touched in months. Detection and enforcement happen automatically, not through tickets and follow-ups.
**CI intelligence** — [Affected detection](/docs/concepts/mental-model#affected-commands), [remote caching](/docs/concepts/how-caching-works), and [distributed task execution](/docs/concepts/ci-concepts/parallelization-distribution) work across the full graph, not just within a single repo.
**AI agents** — AI coding agents are [dramatically less effective in polyrepos](https://youtu.be/alIto5fqrfk) — they can only see one repo at a time, so cross-repo features require you to manually shuttle context between sessions. A synthetic monorepo gives agents cross-repo visibility, enabling coordinated changes, parallel execution, and automatic PR creation across boundaries. [Self-healing CI](/docs/features/ci-features/self-healing-ci) catches failures automatically.
## When to use a synthetic monorepo vs. a real monorepo
A real monorepo is the best option when you can consolidate. It gives you atomic commits, a single toolchain, and the simplest mental model.
A synthetic monorepo is the better starting point when:
- **Consolidation isn't feasible yet:** team autonomy concerns, divergent CI setups, or hundreds of repos make migration impractical.
- **You need cross-repo visibility now:** you can't wait months for a migration to see how projects relate across teams.
- **Teams need to stay autonomous:** each team keeps their repo, workflow, and release cadence while still participating in a unified graph.
The two aren't mutually exclusive. Start synthetic for org-wide visibility, then consolidate tightly coupled teams into real monorepos where it makes sense.
## Synthetic monorepos with Nx Polygraph
Nx implements synthetic monorepos through [Nx Polygraph](/docs/enterprise/polygraph). Polygraph connects existing repositories into a unified, intelligent graph that powers the visibility, coordination, and CI features described above. It works with any repo, even those that don't use Nx, and requires zero changes to target repos.
Learn more about [getting started with Nx Polygraph](/docs/enterprise/polygraph).
@@ -65,7 +65,7 @@ This becomes even more evident when you run tasks in parallel. You cannot just n
![task-graph-execution](../../../assets/concepts/mental-model/task-graph-execution.svg)
Nx allows you to define task dependencies in the form of "rules", which are then followed when running tasks. There's a [detailed recipe](/docs/guides/tasks--caching/defining-task-pipeline) but here's the high-level overview:
Define task dependencies in the form of "rules", which are then followed when running tasks. There's a [detailed recipe](/docs/guides/tasks--caching/defining-task-pipeline) but here's the high-level overview:
```jsonc title="nx.json"
{
@@ -55,7 +55,7 @@ The configuration for package manager workspaces varies based on which package m
Defining the `workspaces` property in the root `package.json` file lets npm know to look for other `package.json` files in the specified folders. With this configuration in place, all the dependencies for the individual projects will be installed in the root `node_modules` folder when `npm install` is run in the root folder. Also, the projects themselves will be linked in the root `node_modules` folder to be accessed as if they were npm packages.
If you want to reference a local library project with its own `build` task, you should include the library in the `devDependencies` of the application's `package.json` with `*` specified as the library's version. `*` tells npm to use whatever version of the project is available.
If you want to reference a local library project with its own `build` task, you should include the library in the `devDependencies` of the application/library's `package.json` with `*` specified as the library's version. `*` tells npm to use whatever version of the project is available.
```json title="/apps/my-app/package.json"
{
@@ -76,7 +76,7 @@ If you want to reference a local library project with its own `build` task, you
Defining the `workspaces` property in the root `package.json` file lets yarn know to look for other `package.json` files in the specified folders. With this configuration in place, all the dependencies for the individual projects will be installed in the root `node_modules` folder when `yarn` is run in the root folder. Also, the projects themselves will be linked in the root `node_modules` folder to be accessed as if they were npm packages.
If you want to reference a local library project with its own `build` task, you should include the library in the `devDependencies` of the application's `package.json` with `workspace:*` specified as the library's version. [`workspace:*` tells yarn that the project is in the same repository](https://yarnpkg.com/features/workspaces) and not an npm package. You want to specify local projects as `devDependencies` instead of `dependencies` so that the library is not included twice in the production bundle of the application.
If you want to reference a local library project with its own `build` task, you should include the library in the `devDependencies` of the application/library's `package.json` with `workspace:*` specified as the library's version. [`workspace:*` tells yarn that the project is in the same repository](https://yarnpkg.com/features/workspaces) and not an npm package. You want to specify local projects as `devDependencies` instead of `dependencies` so that the library is not included twice in the production bundle of the application.
```json title="/apps/my-app/package.json"
{
@@ -97,7 +97,7 @@ If you want to reference a local library project with its own `build` task, you
Defining the `workspaces` property in the root `package.json` file lets bun know to look for other `package.json` files in the specified folders. With this configuration in place, all the dependencies for the individual projects will be installed in the root `node_modules` folder when `bun install` is run in the root folder. Also, the projects themselves will be linked in the root `node_modules` folder to be accessed as if they were npm packages.
If you want to reference a local library project with its own `build` task, you should include the library in the `devDependencies` of the application's `package.json` with `workspace:*` specified as the library's version. [`workspace:*` tells bun that the project is in the same repository](https://bun.sh/docs/install/workspaces) and not an npm package. You want to specify local projects as `devDependencies` instead of `dependencies` so that the library is not included twice in the production bundle of the application.
If you want to reference a local library project with its own `build` task, you should include the library in the `devDependencies` of the application/library's `package.json` with `workspace:*` specified as the library's version. [`workspace:*` tells bun that the project is in the same repository](https://bun.sh/docs/install/workspaces) and not an npm package. You want to specify local projects as `devDependencies` instead of `dependencies` so that the library is not included twice in the production bundle of the application.
```json title="/apps/my-app/package.json"
{
@@ -187,7 +187,7 @@ Each project's `tsconfig.json` file should extend the `tsconfig.base.json` file
}
```
Each project's `tsconfig.lib.json` file extends the project's `tsconfig.json` file and adds `references` to the `tsconfig.lib.json` files of project dependencies.
Each project's `tsconfig.lib.json` file extends the `tsconfig.base.json` file and adds `references` to the `tsconfig.lib.json` files of project dependencies.
```jsonc title="packages/cart/tsconfig.lib.json"
{
@@ -68,18 +68,80 @@ The following permissions are required for Nx Cloud to work:
Repository permissions:
- `Administration: Read & Write`
- `Checks: Read & Write`
- `Contents: Read & Write`
- `Pull requests: Read & Write`
- `Checks: Read Only`
- `Commit Statuses: Read & Write`
- `Commit Statuses: Read`
- `Issues: Read & Write`
- `Metadata: Read Only`
- `Metadata: Read`
- `Pull requests: Read & Write`
- `Workflows: Read & Write`
Organization permissions:
- `Administration: Read Only`
- `Members: Read Only`
### Administration (write)
**Used for:** Creating new repositories with a pre-configured Nx workspace during initial onboarding.
**When it's used:** Only when you explicitly choose to create a new workspace through Nx Cloud's setup flow. [Single tenant instances](/docs/enterprise/single-tenant/overview) can safely forego this scope and will only lose the ability to create new workspaces through the app.
### Checks (write)
**Used for:** Updating CI run statuses so you can see the progress and results of your Nx Cloud pipeline executions directly in GitHub. Also used for Self-Healing CI status check runs in PRs.
**When it's used:** Automatically during CI runs to provide real-time status updates.
### Contents (read & write)
**Used for:**
- **Read:** Detecting your workspace's current Nx version to ensure compatibility. Reading files for Self-Healing CI.
- **Write:** Adding Nx Cloud configuration (`nxCloudId` or access token) to your repository during setup. Creating commits and pushing fixes for Self-Healing CI.
**When it's used:** During initial setup and configuration, and regularly if Self-Healing CI is enabled.
### Commit statuses (read)
**Used for:** Reading commit status information to coordinate with other CI tools and provide accurate pipeline context.
**When it's used:** During CI pipeline executions to gather context about your commits.
### Issues (read & write)
**Used for:** PR comments (GitHub uses the Issues API for PR comments — see "Pull requests" below for more detail).
**When it's used:** During CI runs and when posting status comments.
### Metadata (read)
**Used for:** Accessing basic repository information (name, description, visibility). This is a required baseline permission for most GitHub App functionality.
### Pull requests (read & write)
**Used for:**
- **Read:** Gathering branch information, SHAs, and metadata necessary for CI pipeline execution and distributed task coordination.
- **Write:** Posting comments on PRs with CI pipeline status, command results, and Self-Healing CI fixes. Creating PRs during initial Nx Cloud setup. Creating demo PRs for optional features like Self-Healing CI (only when you opt in).
**When it's used:** Read operations occur during CI runs. Write operations occur during setup and when posting status comments.
### Workflows (write)
**Used for:** Automatically configuring GitHub Actions workflow files when you opt in to features like Self-Healing CI and distributed task execution.
**When it's used:** Only when you explicitly enable these features through the Nx Cloud interface.
## Your Data and Security
Most information accessed through these permissions is used transiently during operations and is not stored. Limited version control metadata (such as branch names, SHAs, and commit information) may be stored as part of your CI pipeline execution records for analytics and debugging purposes.
[Nx Cloud is SOC2 Type II certified](https://security.nx.app). We implement industry-standard security practices including encryption at rest and in transit, access logging, and regular security audits.
You can revoke access to the Nx Cloud GitHub app at any time through your GitHub settings. Write operations (creating repos, posting comments, modifying workflows) only occur when explicitly triggered by your actions or when you opt in to specific features.
## Connect Your Nx Cloud Installation
Provide the following values to your developer productivity engineer so they can help connect Nx Cloud to your custom GitHub app:
@@ -4,7 +4,7 @@ description: Create and publish custom Nx Conformance rules to your Nx Cloud org
filter: 'type:Guides'
---
[Nx Cloud Enterprise](https://nx.dev/enterprise) allows you to publish your organization's [Nx Conformance](/docs/enterprise/conformance) rules to your Nx Cloud Organization, and consume them in any of your other Nx Workspaces without having to deal with the complexity and friction of dealing with a private NPM registry or similar. Authentication is handled automatically through your Nx Cloud connection and rules are downloaded and applied based on your preferences configured in the Nx Cloud UI.
[Nx Cloud Enterprise](https://nx.dev/enterprise) lets you publish custom [Nx Conformance](/docs/enterprise/conformance) rules to your Nx Cloud Organization and consume them across workspaces — no private NPM registry needed. See [Configure Conformance Rules in Nx Cloud](/docs/enterprise/configure-conformance-rules-in-nx-cloud) for how to manage published rules in the UI.
Let's create a custom rule which we can then publish to Nx Cloud. We will first create a new library project to contain our rule (and any others we might create in the future):
@@ -128,6 +128,184 @@ Nx uses the paths from `tsconfig.base.json` when running plugins locally, but us
![vscode-schematics-debug](../../../assets/nx-console/vscode-schematics-debug.png)
## Generator Schema Properties
Beyond the standard [JSON Schema](https://json-schema.org/) properties like `type`, `description`, `enum`, and `default`, Nx recognizes several custom properties in your `schema.json` that control CLI prompting behavior and how [Nx Console](/docs/getting-started/editor-setup) renders the generator form.
### `$default`
Provides a dynamic default value from a runtime source. Used to map positional CLI arguments and other context to schema properties.
```json
// schema.json
{
"properties": {
"name": {
"type": "string",
"$default": {
"$source": "argv",
"index": 0
}
}
}
}
```
| Source | Description |
| ----------------------------------- | ------------------------------------------------------------------------------------------- |
| `{ "$source": "argv", "index": 0 }` | Uses the positional CLI argument at the given index |
| `{ "$source": "projectName" }` | Uses the current project name. Also triggers project autocomplete in the CLI and Nx Console |
| `{ "$source": "workingDirectory" }` | Uses the current working directory relative to the workspace root |
| `{ "$source": "unparsed" }` | Collects any extra arguments not matched by other schema properties |
### `x-prompt`
Defines an interactive prompt shown when the option is not provided on the command line. Can be a simple string or a structured object for more control.
```json
// schema.json
{
"properties": {
"style": {
"type": "string",
"description": "The file extension to be used for style files.",
"x-prompt": {
"message": "Which stylesheet format would you like to use?",
"type": "list",
"items": [
{ "value": "css", "label": "CSS" },
{ "value": "scss", "label": "SASS (.scss)" },
{ "value": "less", "label": "LESS" }
]
}
}
}
}
```
**Short form:** `"x-prompt": "What name would you like to use?"` — displays a simple text prompt.
**Long form object properties:**
| Property | Type | Description |
| ------------- | ------------------------------------------------ | ----------------------------------------------------- |
| `message` | `string` | The prompt text displayed to the user |
| `type` | `string` | Prompt type: `"input"`, `"list"`, or `"confirmation"` |
| `multiselect` | `boolean` | Allow selecting multiple items (for `"list"` type) |
| `items` | `(string \| { label: string, value: string })[]` | Choices for `"list"` type prompts |
In Nx Console, the `message` is shown as a tooltip on the field and `items` labels are shown as option descriptions.
### `x-priority`
Controls the visibility and ordering of an option in the Nx Console Generate form.
```json
// schema.json
{
"properties": {
"name": {
"type": "string",
"x-priority": "important"
},
"skipFormat": {
"type": "boolean",
"x-priority": "internal"
}
}
}
```
| Value | Effect |
| ------------- | ------------------------------------------------------------- |
| `"important"` | Field appears near the top of the form, after required fields |
| `"internal"` | Field is hidden from the form by default |
Options in the Nx Console form are sorted: **required** > **important** > **regular** > **deprecated** > **internal**.
### `x-deprecated`
Marks an option as deprecated. Deprecated options are sorted to the bottom of the form in Nx Console and display a warning.
```json
// schema.json
{
"properties": {
"oldOption": {
"type": "string",
"x-deprecated": "Use 'newOption' instead."
}
}
}
```
The value can be `true` (boolean) or a string with the deprecation reason/migration guidance.
### `x-dropdown`
Tells both the CLI and Nx Console to present a dropdown populated with workspace data.
```json
// schema.json
{
"properties": {
"projectName": {
"type": "string",
"x-dropdown": "projects"
}
}
}
```
Currently only `"projects"` is supported, which shows all projects in the workspace.
{% aside type="note" title="Automatic project autocomplete" %}
The CLI and Nx Console also automatically provide project autocomplete for any property named `project` or `projectName`, or that has `$default` set to `{ "$source": "projectName" }` — even without `x-dropdown`.
{% /aside %}
### `x-hint`
Displays a hint popover next to the field label in the Nx Console Generate form. Use this for brief contextual guidance that doesn't belong in the main `description`.
```json
// schema.json
{
"properties": {
"name": {
"type": "string",
"x-hint": "You can provide a nested path like my-dir/my-lib"
}
}
}
```
### `x-completion-type` and `x-completion-glob`
These properties are used by Nx Console's language server to provide autocomplete suggestions when editing configuration files like `project.json` or `nx.json`.
```json
// schema.json
{
"properties": {
"tsConfig": {
"type": "string",
"x-completion-type": "file",
"x-completion-glob": "tsconfig*.json"
}
}
}
```
| `x-completion-type` value | Description |
| ------------------------- | ------------------------------------------------------------------------- |
| `"file"` | Autocomplete with file paths (optionally filtered by `x-completion-glob`) |
| `"directory"` | Autocomplete with directory paths |
| `"projects"` | Autocomplete with workspace project names |
| `"targets"` | Autocomplete with available target names |
| `"targetsWithDeps"` | Autocomplete targets, including `^target` syntax for dependencies |
| `"tags"` | Autocomplete with project tags |
| `"projectTarget"` | Autocomplete with `project:target` format |
## Generator Utilities
The [`@nx/devkit` package](/docs/reference/devkit) provides many utility functions that can be used in generators to help with modifying files, reading and updating configuration files, and working with an Abstract Syntax Tree (AST).
@@ -11,12 +11,10 @@ src="https://youtu.be/NF1__N_snog"
title="Remote Caching with Nx Replay"
/%}
Repeatedly rebuilding and retesting the same code is costly — not just in terms of wasted resources, but also in terms of developer time. To solve this, Nx includes a sophisticated computation caching system that ensures **code is never rebuilt twice**, saving you both time and resources.
Nx [caches task results locally](/docs/features/cache-task-results) to avoid rebuilding the same code twice. Remote caching extends this by **sharing the cache across your team and CI**.
![Diagram showing Teika sharing his cache with CI, Kimiko and James](../../../../assets/features/distributed-caching.svg)
By default, Nx [caches task computations locally](/docs/features/cache-task-results), but the biggest benefit comes from **sharing this cache across your team and in CI**.
- **Zero config** and **secure** by default
- Drastically **speeds up task execution times** during local development, and more critically in CI
- **Saves money on CI/CD costs** by reducing the number of tasks that need to be executed (we observed 30-70% faster CI & half the cost)
@@ -16,7 +16,7 @@ Nx Cloud Self-Healing CI is an **AI-powered system that automatically detects, a
- **Improves Time to Green (TTG):** Automatically proposes fixes when tasks fail, significantly reducing the time to get your PR merge-ready. No more babysitting PRs.
- **Keeps You in the Flow:** Get notified about failed PRs and proposed fixes via PR/MR comments or directly in your editor with Nx Console (VS Code, Cursor, or WebStorm). Review, approve, and keep working while AI handles the rest.
- **Leverages Deep Context:** AI agents understand your workspace structure, project relationships, and build configurations through Nx's project graph and metadata.
- **Leverages Deep Context:** AI agents understand your workspace structure, project relationships, and build configurations through the Nx [project graph](/docs/features/explore-graph) and metadata.
- **Non-Invasive Integration:** Works with your existing CI provider without overhauling your current setup.
## Enable Self-Healing CI
@@ -122,6 +122,33 @@ steps:
{% /tabitem %}
{% tabitem label="Bitbucket Pipelines" %}
```yaml
# bitbucket-pipelines.yml
image: node:22
pipelines:
pull-requests:
'**':
- step:
name: CI
script:
# Your existing steps which start-ci-run, install
# dependencies, etc.
# These are just illustrative examples...
- npx nx-cloud start-ci-run
- npm ci
- npx nx affected -t lint test build
after-script:
# NEW: Add this section at the end of your step
# IMPORTANT: after-script runs regardless of step success/failure
# so it's like if: always() on GitHub
- npx nx fix-ci
```
{% /tabitem %}
{% /tabs %}
> NOTE: If all tasks succeed then the `fix-ci` command becomes a no-op automatically, so that is why "always" is recommended.
@@ -182,31 +209,62 @@ Tasks matching these patterns will also have high-confidence, verified code chan
Tasks matching these patterns will **never** have code changes auto-applied, even if they match the include patterns or presets specified above. For example: `*e2e*`.
## Customization with CLAUDE.md
## Configuration with SELF_HEALING.md
Create a `CLAUDE.md` file in your repository root to provide additional context to the AI agent:
Create a `.nx/SELF_HEALING.md` file in your repository to provide project-specific instructions to the Self-Healing CI agent. This file contains freeform markdown that the AI agent reads and interprets naturally.
### Failure Classification Rules
{% aside title="Why a dedicated file?" type="note" %}
Using `.nx/SELF_HEALING.md` instead of `AGENTS.md` (or equivalent) separates CI-specific instructions from local development context. The file lives in the `.nx` directory alongside other Nx Cloud configuration.
{% /aside %}
Override how the AI categorizes failures:
### Example SELF_HEALING.md
```markdown
## Failure Classification
# Self-Healing Configuration
- Failures in `**/migrations/**` should be classified as `environment_state`
- Test timeouts in e2e tests are usually `flaky_task`
## Confidence Rules
- Fixes involving "test" targets should require high confidence
- Formatting fixes can be applied with medium confidence
## Off-Limits Areas
- `/src/generated/` - auto-generated, do not modify
- `/legacy/` - requires manual review
## Fix Preferences
- Prefer updating ESLint rules over adding disable comments
- For type errors, prefer explicit types over `any`
## Context
See ARCHITECTURE.md for module boundaries.
```
### Predefined Fixes
### What to Include
Specify deterministic solutions for common failures:
| Section | Purpose | Example |
| -------------------- | --------------------------------------------------- | ---------------------------------------------------------------------------- |
| **Confidence Rules** | Override how the AI categorizes failure severity | "Failures in `**/migrations/**` should be classified as `environment_state`" |
| **Off-Limits Areas** | Directories or files the agent should never modify | "`/src/generated/` - auto-generated code" |
| **Fix Preferences** | Guide the agent's approach to common issues | "Prefer updating ESLint rules over adding disable comments" |
| **Predefined Fixes** | Specify deterministic solutions for known failures | "For lint failures, always try running `nx lint --fix` first" |
| **Context** | Reference other documentation the agent should read | "See ARCHITECTURE.md for module boundaries" |
```markdown
## Predefined Fixes
### Using CLAUDE.md
- For lint failures, always try running `nx lint --fix` first
- Format failures should use `nx format:write`
```
If your repository already has a `CLAUDE.md` file at the root, the Self-Healing CI agent will read it for additional context. When both files exist:
- **SELF_HEALING.md takes precedence** for any conflicting instructions
- Both files are read, so general context in `CLAUDE.md` is still available
- CI-specific instructions should go in `SELF_HEALING.md`
This allows teams to maintain `CLAUDE.md` for local development workflows while using `SELF_HEALING.md` for CI-specific behavior.
### Viewing Configuration Status
After a CI run, navigate to the pipeline execution in Nx Cloud and check the **Configurations** tab to see whether `SELF_HEALING.md` was detected and applied.
## Receiving Fix Notifications
@@ -1,180 +1,82 @@
---
title: 'Enhance Your LLM'
description: 'Learn how Nx enhances your AI assistant by providing rich workspace metadata, architectural insights, and project relationships to make your LLM smarter and more context-aware.'
title: 'Enhance Your AI Coding Agent'
description: 'Learn how Nx enhances your AI assistant by providing rich workspace metadata, architectural insights, and CI integration for autonomous workflows.'
sidebar:
order: 3
filter: 'type:Features'
---
{% youtube src="https://youtu.be/dRQq_B1HSLA" title="We Just Shipped the Monorepo MCP for Copilot" /%}
AI agents are moving beyond autocomplete. They can now operate independently across projects. But most setups hit a wall: agents lack workspace context (seeing files, not architecture), generate inconsistent code, and have a hard time to interact with CI.
Monorepos [provide an ideal foundation for AI-powered development](https://nx.dev/blog/nx-and-ai-why-they-work-together), enabling cross-project reasoning and code generation. However, without proper context, **LLMs struggle to understand your workspace architecture**, seeing only individual files rather than the complete picture.
Nx monorepos solve this by enabling cross-project reasoning and by providing the structured metadata and CI integration that agents need to work autonomously:
Nx transforms your AI assistant by providing rich workspace metadata that enables it to:
- Deep **workspace architecture** understanding and project relationships
- **Code generators** for fast, predictable scaffolding
- **CI pipeline integration** to fix failures autonomously
- The ability to **iterate until CI is green** without human intervention
- Understand your **workspace architecture** and project relationships
- Identify **project owners** and team responsibilities
- Access **Nx documentation** for accurate guidance
- Leverage **code generators** for consistent scaffolding
- Connect to your **CI pipeline** to help fix failures
## Setup
The goal is to transform your AI assistant from a generic code helper into an architecturally-aware collaborator that understands your specific workspace structure and can make intelligent, context-aware decisions.
## How Nx MCP Enhances Your LLM
The [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) is an open standard that enables AI models to interact with your development environment through a standardized interface. Nx implements an MCP server via the [Nx Console](/docs/getting-started/editor-setup) that exposes workspace metadata to compatible AI assistants like GitHub Copilot, Claude, and others.
With the Nx MCP server, your AI assistant gains a "map" of your entire system being able to go from just reasoning at the file level to seeing the higher-level picture. This allows the LLM to move between different abstraction levels - from high-level architecture down to specific implementation details:
![Different abstraction levels](../../../assets/features/nx-ai-abstraction-levels.avif)
The Nx MCP server exposes tools for workspace analysis, code generation, documentation lookup, and CI/CD analytics. For a complete list of available tools and their descriptions, see the [Nx MCP Server Reference](/docs/reference/nx-mcp#available-tools).
## Setting Up Nx MCP
To configure Nx for AI agents and AI-Assistants, run the following command:
To configure your Nx workspace for AI agents, run:
```shell
npx nx configure-ai-agents
```
This configures Nx Console which automatically configures and serves the Nx MCP server for you if you're using VSCode or Cursor. It also sets up the corresponding AI agent configuration files (e.g. `CLAUDE.md`, `AGENTS.md`,...).
This sets up:
### IDE Setup
- **Agent configuration files**: `CLAUDE.md`, `AGENTS.md` with workspace-specific guidelines
- **Agent skills**: Domain-specific knowledge for monorepo workflows — workspace exploration, code generation, task execution, CI monitoring, and package linking. Skills teach agents _how_ to work with Nx rather than dumping data into context.
- **Nx MCP server**: Provides connectivity to Nx Cloud CI pipelines, self-healing fixes, running processes, and Nx documentation — things agents can't easily reach on their own
For VS Code, Cursor, and JetBrains IDE users:
## What This Enables
1. Install [Nx Console](/docs/getting-started/editor-setup) from the marketplace
2. You'll receive a notification to "Improve Copilot/AI agent with Nx-specific context"
3. Click "Yes" to configure the MCP server
### Self-Healing CI Integration
![VS Code showing the Nx MCP installation prompt](../../../assets/features/copilot-mcp-install.avif)
Nx Cloud provides AI-powered [Self-Healing CI](/docs/features/ci-features/self-healing-ci) that analyzes failed runs and proposes verified fixes. With `configure-ai-agents`, your local agent connects to this CI counterpart via skills and the Nx MCP, gaining full context about run information, failures, and suggested fixes.
If you miss the notification, run the `nx.configureMcpServer` (`Nx: Setup MCP Server` in JetBrains) command from the command palette (Cursor: `Ctrl/Cmd + Shift + P`, JetBrains IDEs: `Ctrl/Cmd + Shift + A`).
### Other MCP-Compatible Clients
For other MCP-compatible clients like Claude Desktop, Claude Code, or Warp, you can configure the Nx MCP server manually. See the [Nx MCP Server Reference](/docs/reference/nx-mcp#client-specific-setup) for detailed setup instructions for each client.
Quick example for Claude Code:
```shell
claude mcp add nx-mcp npx nx-mcp@latest
```
## Powerful Use Cases
### Understanding Your Workspace Architecture
{% youtube src="https://youtu.be/RNilYmJJzdk" title="Nx Just Made Your LLM Way Smarter" /%}
Ask your AI assistant about your workspace structure and get detailed, accurate responses about projects, their types, and relationships:
Your agent can autonomously iterate until CI passes:
```text
What is the structure of this workspace?
How are the projects organized?
Commit this work, create a PR, and monitor CI until it's green.
```
With Nx MCP, your AI assistant can:
The workflow:
- Identify applications and libraries in your workspace
- Understand project categorization through tags
- Recognize technology types (feature, UI, data-access)
- Determine project ownership and team responsibilities
1. Agent pushes changes and creates PR
2. Monitors CI pipeline
3. Receives failure context from Nx Cloud and Self-Healing CI
4. Accepts proposed fix or pulls context locally and manually applies it
5. Repeats until CI is green
![Example of LLM understanding project structure](../../../assets/features/nx-ai-example-project-data.avif)
This reduces context-switching—you review the final PR rather than intervening at each failure.
You can also get informed suggestions about where to implement new functionality:
### Workspace Architecture Understanding
```text
Where should I implement a feature for adding products to cart?
```
Nx exposes the project graph and relevant metadata to AI agents. This helps them move faster and more precisely:
![Example of LLM providing implementation guidance](../../../assets/features/nx-ai-example-data-access-feature.avif)
- Identify all applications and libraries in the workspace
- Understand project relationships and dependencies
- Recognize project types and ownership via tags
- Determine which projects are affected by changes
- Suggest where to implement new functionality based on existing structure
Learn more about workspace architecture understanding in our blog post [Nx Just Made Your LLM Way Smarter](https://nx.dev/blog/nx-just-made-your-llm-smarter).
This architectural awareness is critical for agents operating in large monorepos where understanding project relationships determines the quality of generated code.
### Instant CI Failure Resolution
### Predictable, Fast Code Generation
{% youtube src="https://youtu.be/fPqPh4h8RJg" title="Connect Your Editor, CI and LLMs" /%}
AI-generated code is token-intensive, slow, and not guaranteed to align with patterns in other projects. Nx generators solve this by providing predictable scaffolding that agents can invoke and then adapt.
When a CI build fails, Nx Console can notify you directly in your editor:
Your AI agent can:
![Nx Console shows the notification of the CI failure](../../../assets/features/ci-notification.avif)
1. Find generators from [Nx plugins](/docs/plugin-registry) or custom [local workspace generators](/docs/extending-nx/local-generators)
2. Run the generator with correct options
3. Make small adjustments based on the specific situation
Your AI assistant can then:
This approach is faster, produces consistent code across projects, and reduces hallucinations.
1. Access detailed information from Nx Cloud about the failed build
2. Analyze your git history to understand what changed in your PR
3. Understand the error context and affected files
4. Help implement the fix right in your editor
## Learn More
This integration dramatically improves the development velocity because you get immediately notified when an error occurs, you don't even have to leave your editor to understand what broke, and the LLM can help you implement or suggest a possible fix.
Learn more about CI integration in our blog post [Save Time: Connecting Your Editor, CI and LLMs](https://nx.dev/blog/nx-editor-ci-llm-integration).
### Smart Code Generation with AI-Enhanced Generators
{% youtube src="https://youtu.be/PXNjedYhZDs" title="Enhancing Nx Generators with AI" /%}
Nx generators provide predictable code scaffolding, while AI adds intelligence and contextual understanding. Instead of having the AI generate everything from scratch, you get the best of both worlds:
```text
Create a new React library into the packages/orders/feat-cancel-orders folder
and call the library with the same name of the folder structure. Afterwards,
also connect it to the main shop application.
```
Your AI assistant will:
1. Identify the appropriate generator and its parameters
2. Open the Nx Console Generate UI with preset values
3. Let you review and customize the options
4. Execute the generator and help integrate the new code with your existing projects
![LLM invoking the Nx generate UI](../../../assets/features/llm-nx-generate-ui.avif)
This approach ensures consistent code that follows your organization's best practices while still being tailored to your specific needs. Learn more about AI-enhanced generators in our blog post [Enhancing Nx Generators with AI](https://nx.dev/blog/nx-generators-ai-integration).
### Documentation-Aware Configuration
{% youtube src="https://youtu.be/V2W94Sq_v6A?si=aBA-eppEw0fHrh5O&t=388" title="Making Cursor Smarter with an MCP Server" /%}
Get accurate guidance on Nx configuration without worrying about hallucinations or outdated information:
```text
Can you configure Nx release for the packages of this workspace?
Update nx.json with the necessary configuration using conventional commits
as the versioning strategy.
```
The AI assistant will:
1. Query the Nx docs for the latest information on release configuration
2. Understand your workspace structure to identify packages
3. Generate the correct configuration based on your specific needs
4. Apply the changes to your nx.json file
Learn more about documentation-aware configuration in our blog post [Making Cursor Smarter with an MCP Server For Nx Monorepos](https://nx.dev/blog/nx-made-cursor-smarter).
### Cross-Project Dependency Analysis
{% youtube src="https://youtu.be/dRQq_B1HSLA?si=lhHsjRvwgijC1IL8&t=186" title="Nx MCP Now Available for VS Code Copilot" /%}
Understand the impact of changes across your monorepo with questions like:
```text
If I change the public API of feat-product-detail, which other projects
might be affected by that change?
```
Your AI assistant can:
- Analyze the project graph to identify direct and indirect dependencies
- Visualize affected projects using the `nx_visualize_graph` tool
- Suggest strategies for refactoring that minimize impact
- Identify which teams would need to be consulted for major changes
This architectural awareness is particularly powerful in larger monorepos where understanding project relationships is crucial for making informed development decisions.
Learn more about dependency analysis in our blog post [Nx MCP Now Available for VS Code Copilot](https://nx.dev/blog/nx-mcp-vscode-copilot).
- [Autonomous AI Agents at Scale](https://nx.dev/blog/ai-agents-and-continuity): Infrastructure requirements for AI agent workflows
- [Why Nx and AI Work So Well Together](https://nx.dev/blog/nx-and-ai-why-they-work-together): The foundation for AI-powered development
- [Nx MCP Server Reference](/docs/reference/nx-mcp): Complete tool reference and setup instructions
@@ -83,12 +83,10 @@ If your repository is using package manager workspaces, Nx will use those settin
### Inferred Tasks with Tooling Plugins
Nx provides [plugins](/docs/concepts/nx-plugins) for tools that run tasks, like Vite, TypeScript, Playwright or Jest. These plugins can automatically [infer the Nx-specific task configuration](/docs/concepts/inferred-tasks) based on the tooling configuration files that already exist.
Nx [plugins](/docs/concepts/nx-plugins) for tools like Vite, TypeScript, Playwright, and Jest automatically [infer task configuration](/docs/concepts/inferred-tasks) from your existing tooling config files — keeping them as the single source of truth.
In the example below, because the `/apps/cart/vite.config.ts` file exists, Nx knows that the `cart` project can run a `build` task using Vite. If you expand the `build` task, you can also see that Nx configured the output directory for the [cache](/docs/features/cache-task-results) to match the `build.outDir` provided in the Vite configuration file.
With inferred tasks, you can keep your tooling configuration file as the one source of truth for that tool's configuration, instead of adding an extra layer of configuration on top.
```ts
// /apps/cart/vite.config.ts
/// <reference types='vitest' />
@@ -1,6 +1,6 @@
---
title: 'Building and Testing Angular Apps in Nx'
description: In this tutorial you'll create a frontend-focused workspace with Nx.
description: In this tutorial you'll create a frontend-focused monorepo with Nx.
sidebar:
label: 'Angular Monorepo'
filter: 'type:Guides'
@@ -106,7 +106,7 @@ Root project 'gradle-tutorial'
## Add Nx
Nx is a build system with built in tooling and advanced CI capabilities. It helps you maintain and scale monorepos,
Nx is a monorepo platform with built in tooling and advanced CI capabilities. It helps you maintain and scale monorepos,
both locally and on CI. We will explore the features of Nx in this tutorial by adding it to the Gradle workspace above.
To add Nx, run
@@ -2,7 +2,7 @@
title: 'Building and Testing React Apps in Nx'
sidebar:
label: 'React Monorepo'
description: In this tutorial you'll create a frontend-focused workspace with Nx.
description: In this tutorial you'll create a frontend-focused monorepo with Nx.
filter: 'type:Guides'
---

Some files were not shown because too many files have changed in this diff Show More