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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/docs/src/content/agents/mcp-server.md
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,7 @@ A tool answers with a JSON object, sent as text. Most tools answer `{ "markdown"
| ----------------------------------------------------------------------------------------------------------- | ------------------------------ |
| Every tool whose name starts with `list-` or `inspect-` | `{ "markdown": "..." }` |
| `analog-routes`, `analog-api-routes`, `analog-server-functions`, `analog-server-calls` and `analog-content` | `{ "markdown": "..." }` |
| `wait-for` | `{ "markdown": "..." }` |
| `wait-for` and `lint-templates` | `{ "markdown": "..." }` |
| `build-meta` | The project and version fields |
| `snapshot` | `{ "markdown": "..." }` |

Expand Down
24 changes: 23 additions & 1 deletion apps/docs/src/content/agents/tools.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ description: Every agent tool the devtools expose, grouped by inspector, with wh
---

<ngmd-hero title="Tools" logo="https://cdn.simpleicons.org/modelcontextprotocol/71717A" gradient>
Ninety-seven tools, grouped by inspector. Each one answers a question you would otherwise answer by clicking through the panel.
Ninety-eight tools, grouped by inspector. Each one answers a question you would otherwise answer by clicking through the panel.
</ngmd-hero>

# Tools
Expand Down Expand Up @@ -231,6 +231,28 @@ The page reads the blocks with `ng.ɵgetControlFlowBlocks`, which Angular 22.0 a

Each page lists at most 50 blocks, with a note on how many more are not shown, and says when the page has more than the 500 blocks it reports. The whole answer stops at 20,000 characters. If several tabs report, pass `page` to read one. The **&#64;for blocks** list on the [Components](../inspectors/components.md#for-blocks) page has every block.

### lint-templates

Checks the component templates in your source, inline `template` and `templateUrl` files, with the project's own `@angular/compiler`. Reads: source.

| Argument | Required | Value |
| ---------- | -------- | ---------------------------------------------------------------------- |
| `rule` | no | Only findings of this rule, one of the ids below. |
| `file` | no | Only findings in files whose path contains this text, such as `cart/`. |
| `severity` | no | Only findings of this severity: `error`, `warning` or `info`. |
| `limit` | no | How many findings to list, 1 to 200. Defaults to 50. |

| Rule | Severity | Finds |
| ---------------------- | --------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `template-parse-error` | `error` | An error the template parser reports, such as an unclosed element, a broken expression or `@for` without `track`. |
| `track-by-index` | `warning` | `@for` that tracks by `$index` (or an alias of it) while its body reads fields of the item, so the items are objects. Rows that bind a form directive, such as `[formControlName]="$index"` or `[formField]`, are skipped. |
| `img-missing-alt` | `warning` | `<img>` without `alt`, `[alt]` or `[attr.alt]`, unless it has `role="presentation"`, `role="none"` or `aria-hidden="true"`. |
| `click-without-key` | `warning` | `(click)` on a plain element such as `<div>` or `<span>` with no `keydown`, `keyup` or `keypress` handler. Buttons, links, form controls and components are skipped. |
| `legacy-control-flow` | `info` | `*ngIf`, `*ngFor`, `*ngSwitchCase` or `*ngSwitchDefault` where `@if`, `@for` or `@switch` would do. Skipped before Angular 17. |
| `ngclass-ngstyle` | `info` | `ngClass` or `ngStyle` where `[class]` or `[style]` bindings would do. |

Each finding gives its severity, rule, `file:line` and a fix. Errors come first. A template whose text has not changed since the last call is not parsed again. A backtick template built with `${}` is skipped. If the project has no `@angular/compiler` installed, the answer says so and lists nothing.

### hydration <ngmd-badge variant="alpha">Action</ngmd-badge>

Hydration of each page that was rendered on the server: how many component hosts were hydrated, skipped with `ngSkipHydration`, mismatched or created on the client, and how many `@defer` blocks are still dehydrated. It names the skipped and client-created hosts and the dehydrated blocks, at most 30 of each, and each mismatch with a line diff of the server HTML Angular expected (`-`) and the client DOM it found (`+`). Reads: page.
Expand Down
35 changes: 18 additions & 17 deletions apps/docs/src/content/inspectors/components.md
Original file line number Diff line number Diff line change
Expand Up @@ -447,23 +447,24 @@ Press Escape in the filter box to clear it. When the filter matches nothing, **C

## Agent tools

| Tool or resource | Kind | What it does |
| ------------------------------------ | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `pangular:get-components` | tool | Lists components and directives from source, with selector, kind, inputs, outputs, change detection, file and line. |
| `pangular:highlight` | tool | Highlights a component in the page and selects it. Takes an instance id, class name, host tag or CSS selector. Also retargets the Signals graph. Lists every instance a name matches. |
| `pangular:inspect-component` | tool | Selects one instance and returns its live detail: inputs, outputs, properties, listeners, directives and injected services. With `path`, reads one nested value instead, with `depth` and `limit`. |
| `pangular:list-components` | tool | Returns the live tree of one page as an indented outline with routed, directive host and hydration markers. Takes a `filter` with the search syntax, a `depth` and `directiveHosts`. |
| `pangular:defer-blocks` | tool | Lists the `@defer` blocks of each page, and flags blocks that failed, blocks stuck on their placeholder and blocks still dehydrated. |
| `pangular:for-blocks` | tool | Lists the `@for` blocks of each page with their item count, track kind and `@empty` block. |
| `pangular:hydration` | tool | Returns the hydration status of each page rendered on the server, and turns the outline on the page on or off. Turning the outline on or off is an action. |
| `pangular:change-detection` | tool | Starts, stops or clears a change detection recording, turns the live views on or off, and returns the slowest and most often checked components, hook, output and afterRender time, the latest cycles, or an export. |
| `pangular:diagnose-change-detection` | tool | What started the recorded cycles, components that depend on zone.js, wasted checks, inputs that break OnPush, and OnPush hints. |
| `pangular:set-component-property` | tool | Writes an input or property of one instance and runs change detection. An action. |
| `pangular:set-signal` | tool | Sets a writable signal that one instance holds as a property. An action. |
| `pangular:find-leaks` | tool | Lists retained destroyed instances, subscriptions still open after their component went away, and live effects and subscriptions per component. |
| `pangular:track-subscriptions` | tool | Starts or stops subscription tracking on a page, like **Track subscriptions**. An action. |
| `pangular:screenshot-component` | tool | Returns an image of one component's host element, drawn by the page from a copy of its DOM and computed styles, with secret fields and marked elements masked. Takes an instance id, class name, host tag or CSS selector. |
| `pangular:component-tree` | resource | The live tree per page, with the detail of the selected instance and the defer blocks. |
| Tool or resource | Kind | What it does |
| ------------------------------------ | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `pangular:get-components` | tool | Lists components and directives from source, with selector, kind, inputs, outputs, change detection, file and line. |
| `pangular:highlight` | tool | Highlights a component in the page and selects it. Takes an instance id, class name, host tag or CSS selector. Also retargets the Signals graph. Lists every instance a name matches. |
| `pangular:inspect-component` | tool | Selects one instance and returns its live detail: inputs, outputs, properties, listeners, directives and injected services. With `path`, reads one nested value instead, with `depth` and `limit`. |
| `pangular:list-components` | tool | Returns the live tree of one page as an indented outline with routed, directive host and hydration markers. Takes a `filter` with the search syntax, a `depth` and `directiveHosts`. |
| `pangular:defer-blocks` | tool | Lists the `@defer` blocks of each page, and flags blocks that failed, blocks stuck on their placeholder and blocks still dehydrated. |
| `pangular:for-blocks` | tool | Lists the `@for` blocks of each page with their item count, track kind and `@empty` block. |
| `pangular:lint-templates` | tool | Lints component templates in source with the project's Angular template parser: parse errors, `@for` tracking objects by `$index`, `<img>` without `alt`, `(click)` without a key handler, `*ngIf`-style directives and `ngClass` or `ngStyle`. |
| `pangular:hydration` | tool | Returns the hydration status of each page rendered on the server, and turns the outline on the page on or off. Turning the outline on or off is an action. |
| `pangular:change-detection` | tool | Starts, stops or clears a change detection recording, turns the live views on or off, and returns the slowest and most often checked components, hook, output and afterRender time, the latest cycles, or an export. |
| `pangular:diagnose-change-detection` | tool | What started the recorded cycles, components that depend on zone.js, wasted checks, inputs that break OnPush, and OnPush hints. |
| `pangular:set-component-property` | tool | Writes an input or property of one instance and runs change detection. An action. |
| `pangular:set-signal` | tool | Sets a writable signal that one instance holds as a property. An action. |
| `pangular:find-leaks` | tool | Lists retained destroyed instances, subscriptions still open after their component went away, and live effects and subscriptions per component. |
| `pangular:track-subscriptions` | tool | Starts or stops subscription tracking on a page, like **Track subscriptions**. An action. |
| `pangular:screenshot-component` | tool | Returns an image of one component's host element, drawn by the page from a copy of its DOM and computed styles, with secret fields and marked elements masked. Takes an instance id, class name, host tag or CSS selector. |
| `pangular:component-tree` | resource | The live tree per page, with the detail of the selected instance and the defer blocks. |

See [Tools](../agents/tools.md) and [Resources](../agents/resources.md).

Expand Down
2 changes: 1 addition & 1 deletion apps/docs/src/content/security.md
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,7 @@ An app can put any text in a value, a URL, a label or an error message, includin

A tool answer that names a page the server does not know lists the pages that report, with their URLs. That answer starts with the first notice too.

Answers that hold no page text carry no notice: `highlight` (instance ids, class names and tags), answers that only say there is no data yet, `lint-pipes` and the source scan tools such as `get-components` (they read your project files, not the page). The `component-tree`, `signal-graph`, `injector-tree`, `ngrx-store`, `forms` and `router` resources carry the notice in an `_untrusted` field at the top of their JSON, also when they return a truncated summary.
Answers that hold no page text carry no notice: `highlight` (instance ids, class names and tags), answers that only say there is no data yet, `lint-pipes`, `lint-templates` and the source scan tools such as `get-components` (they read your project files, not the page). The `component-tree`, `signal-graph`, `injector-tree`, `ngrx-store`, `forms` and `router` resources carry the notice in an `_untrusted` field at the top of their JSON, also when they return a truncated summary.

## Checklist

Expand Down
1 change: 1 addition & 0 deletions packages/devtools/skills/pangular-inspector/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ Pangular Inspector reads a running Angular app through Angular's debug APIs and
| Why does `inject()` fail (NG0201, NullInjectorError)? | `explain-injection` with `selector` and `token` |
| Which route is active, and why did navigation fail? | `inspect-route`, `explain-navigation`, `export-navigation` |
| Is the route config wrong? | `lint-routes`, `list-routes` with `match` |
| Do the templates have errors or a11y problems? | `lint-templates`, with `rule` or `file` to narrow it |
| Why is the form invalid or the submit dead? | `explain-form-invalid`, `explain-field`, `explain-submit` (see `pangular-forms`) |
| What errors and console messages happened? | `list-errors`, `explain-error`, `list-console` (see `pangular-errors`) |
| Which HTTP calls ran, and what did SSR do? | `list-http-calls`, `list-ssr-requests`, `explain-ssr-request` |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
"lint-forms": "forms",
"lint-pipes": "pipes",
"lint-routes": "router",
"lint-templates": "components",
"list-capacitor-calls": "capacitor",
"list-capacitor-plugins": "capacitor",
"list-components": "components",
Expand Down Expand Up @@ -275,6 +276,7 @@
"lint-forms": ["forms"],
"lint-pipes": ["pipes"],
"lint-routes": ["router"],
"lint-templates": ["components"],
"list-capacitor-calls": ["capacitor"],
"list-capacitor-plugins": ["capacitor"],
"list-components": ["components"],
Expand Down
1 change: 1 addition & 0 deletions packages/devtools/src/__tests__/tool-metadata.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,7 @@ describe('agent tool output schemas', () => {
'pangular_analog-server-calls',
'pangular_analog-content',
'pangular_build-meta',
'pangular_lint-templates',
'pangular_wait-for',
'pangular_snapshot',
].includes(name);
Expand Down
1 change: 1 addition & 0 deletions packages/devtools/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -925,6 +925,7 @@ export const AGENT_ENTRIES: Readonly<Record<string, AgentEntry>> = {

// components
'get-components': { kind: 'rpc', inspectors: ['components'] },
'lint-templates': { kind: 'tool', inspectors: ['components'] },
'component-tree': { kind: 'resource', inspectors: ['components'], page: true },
highlight: { kind: 'tool', inspectors: ['components'], page: true },
'inspect-component': { kind: 'tool', inspectors: ['components'], page: true },
Expand Down
3 changes: 3 additions & 0 deletions packages/devtools/src/devframe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import { sanitizeInjectorTree } from './rpc/injector-report.ts';
import { createFrameworkNames, markFramework } from './rpc/framework-tokens.ts';
import { lintPipes, lintPipesText } from './rpc/pipe-lint.ts';
import { cachedPipeLint } from './rpc/pipe-lint-cache.ts';
import { templateLintTool } from './rpc/template-lint-tool.ts';
import { explainPipeOnPage, pipeRecordRequest, pipeRecordText } from './rpc/pipes-agent.ts';
import { createPageOwners } from './rpc/page-owners.ts';
import { trackPageSessions } from './rpc/page-sessions.ts';
Expand Down Expand Up @@ -2955,6 +2956,8 @@ const pangular = defineDevframe({
},
});

agent.registerTool(templateLintTool(ctx.cwd));

agent.registerTool({
id: 'pangular:for-blocks',
title: 'List @for blocks',
Expand Down
Loading
Loading