#
tokens: 49012/50000 52/473 files (page 3/7)
lines: off (toggle) GitHub
raw markdown copy
This is page 3 of 7. Use http://codebase.md/push-based/angular-toolkit-mcp?page={x} to view the full context.

# Directory Structure

```
├── .aiignore
├── .cursor
│   ├── flows
│   │   ├── component-refactoring
│   │   │   ├── 01-review-component.mdc
│   │   │   ├── 02-refactor-component.mdc
│   │   │   ├── 03-validate-component.mdc
│   │   │   └── angular-20.md
│   │   ├── ds-refactoring-flow
│   │   │   ├── 01-find-violations.mdc
│   │   │   ├── 01b-find-all-violations.mdc
│   │   │   ├── 02-plan-refactoring.mdc
│   │   │   ├── 02b-plan-refactoring-for-all-violations.mdc
│   │   │   ├── 03-fix-violations.mdc
│   │   │   ├── 03-non-viable-cases.mdc
│   │   │   ├── 04-validate-changes.mdc
│   │   │   ├── 05-prepare-report.mdc
│   │   │   └── clean-global-styles.mdc
│   │   └── README.md
│   └── mcp.json.example
├── .github
│   └── workflows
│       └── ci.yml
├── .gitignore
├── .nvmrc
├── .prettierignore
├── .prettierrc
├── assets
│   ├── entain-logo.png
│   └── entain.png
├── CONTRIBUTING.MD
├── docs
│   ├── architecture-internal-design.md
│   ├── component-refactoring-flow.md
│   ├── contracts.md
│   ├── ds-refactoring-flow.md
│   ├── getting-started.md
│   ├── README.md
│   ├── tools.md
│   └── writing-custom-tools.md
├── eslint.config.mjs
├── jest.config.ts
├── jest.preset.mjs
├── LICENSE
├── nx.json
├── package-lock.json
├── package.json
├── packages
│   ├── .gitkeep
│   ├── angular-mcp
│   │   ├── eslint.config.mjs
│   │   ├── package.json
│   │   ├── README.md
│   │   ├── src
│   │   │   ├── assets
│   │   │   │   └── .gitkeep
│   │   │   └── main.ts
│   │   ├── tsconfig.app.json
│   │   ├── tsconfig.json
│   │   ├── vitest.config.mts
│   │   └── webpack.config.cjs
│   ├── angular-mcp-server
│   │   ├── eslint.config.mjs
│   │   ├── package.json
│   │   ├── README.md
│   │   ├── src
│   │   │   ├── index.ts
│   │   │   └── lib
│   │   │       ├── angular-mcp-server.ts
│   │   │       ├── prompts
│   │   │       │   └── prompt-registry.ts
│   │   │       ├── tools
│   │   │       │   ├── ds
│   │   │       │   │   ├── component
│   │   │       │   │   │   ├── get-deprecated-css-classes.tool.ts
│   │   │       │   │   │   ├── get-ds-component-data.tool.ts
│   │   │       │   │   │   ├── list-ds-components.tool.ts
│   │   │       │   │   │   └── utils
│   │   │       │   │   │       ├── deprecated-css-helpers.ts
│   │   │       │   │   │       ├── doc-helpers.ts
│   │   │       │   │   │       ├── metadata-helpers.ts
│   │   │       │   │   │       └── paths-helpers.ts
│   │   │       │   │   ├── component-contract
│   │   │       │   │   │   ├── builder
│   │   │       │   │   │   │   ├── build-component-contract.tool.ts
│   │   │       │   │   │   │   ├── models
│   │   │       │   │   │   │   │   ├── schema.ts
│   │   │       │   │   │   │   │   └── types.ts
│   │   │       │   │   │   │   ├── spec
│   │   │       │   │   │   │   │   ├── css-match.spec.ts
│   │   │       │   │   │   │   │   ├── dom-slots.extractor.spec.ts
│   │   │       │   │   │   │   │   ├── element-helpers.spec.ts
│   │   │       │   │   │   │   │   ├── inline-styles.collector.spec.ts
│   │   │       │   │   │   │   │   ├── meta.generator.spec.ts
│   │   │       │   │   │   │   │   ├── public-api.extractor.spec.ts
│   │   │       │   │   │   │   │   ├── styles.collector.spec.ts
│   │   │       │   │   │   │   │   └── typescript-analyzer.spec.ts
│   │   │       │   │   │   │   └── utils
│   │   │       │   │   │   │       ├── build-contract.ts
│   │   │       │   │   │   │       ├── css-match.ts
│   │   │       │   │   │   │       ├── dom-slots.extractor.ts
│   │   │       │   │   │   │       ├── element-helpers.ts
│   │   │       │   │   │   │       ├── inline-styles.collector.ts
│   │   │       │   │   │   │       ├── meta.generator.ts
│   │   │       │   │   │   │       ├── public-api.extractor.ts
│   │   │       │   │   │   │       ├── styles.collector.ts
│   │   │       │   │   │   │       └── typescript-analyzer.ts
│   │   │       │   │   │   ├── diff
│   │   │       │   │   │   │   ├── diff-component-contract.tool.ts
│   │   │       │   │   │   │   ├── models
│   │   │       │   │   │   │   │   └── schema.ts
│   │   │       │   │   │   │   ├── spec
│   │   │       │   │   │   │   │   ├── diff-utils.spec.ts
│   │   │       │   │   │   │   │   └── dom-path-utils.spec.ts
│   │   │       │   │   │   │   └── utils
│   │   │       │   │   │   │       ├── diff-utils.ts
│   │   │       │   │   │   │       └── dom-path-utils.ts
│   │   │       │   │   │   ├── index.ts
│   │   │       │   │   │   ├── list
│   │   │       │   │   │   │   ├── list-component-contracts.tool.ts
│   │   │       │   │   │   │   ├── models
│   │   │       │   │   │   │   │   ├── schema.ts
│   │   │       │   │   │   │   │   └── types.ts
│   │   │       │   │   │   │   ├── spec
│   │   │       │   │   │   │   │   └── contract-list-utils.spec.ts
│   │   │       │   │   │   │   └── utils
│   │   │       │   │   │   │       └── contract-list-utils.ts
│   │   │       │   │   │   └── shared
│   │   │       │   │   │       ├── models
│   │   │       │   │   │       │   └── types.ts
│   │   │       │   │   │       ├── spec
│   │   │       │   │   │       │   └── contract-file-ops.spec.ts
│   │   │       │   │   │       └── utils
│   │   │       │   │   │           └── contract-file-ops.ts
│   │   │       │   │   ├── component-usage-graph
│   │   │       │   │   │   ├── build-component-usage-graph.tool.ts
│   │   │       │   │   │   ├── index.ts
│   │   │       │   │   │   ├── models
│   │   │       │   │   │   │   ├── config.ts
│   │   │       │   │   │   │   ├── schema.ts
│   │   │       │   │   │   │   └── types.ts
│   │   │       │   │   │   └── utils
│   │   │       │   │   │       ├── angular-parser.ts
│   │   │       │   │   │       ├── component-helpers.ts
│   │   │       │   │   │       ├── component-usage-graph-builder.ts
│   │   │       │   │   │       ├── path-resolver.ts
│   │   │       │   │   │       └── unified-ast-analyzer.ts
│   │   │       │   │   ├── ds.tools.ts
│   │   │       │   │   ├── project
│   │   │       │   │   │   ├── get-project-dependencies.tool.ts
│   │   │       │   │   │   ├── report-deprecated-css.tool.ts
│   │   │       │   │   │   └── utils
│   │   │       │   │   │       ├── dependencies-helpers.ts
│   │   │       │   │   │       └── styles-report-helpers.ts
│   │   │       │   │   ├── report-violations
│   │   │       │   │   │   ├── index.ts
│   │   │       │   │   │   ├── models
│   │   │       │   │   │   │   ├── schema.ts
│   │   │       │   │   │   │   └── types.ts
│   │   │       │   │   │   ├── report-all-violations.tool.ts
│   │   │       │   │   │   └── report-violations.tool.ts
│   │   │       │   │   ├── shared
│   │   │       │   │   │   ├── index.ts
│   │   │       │   │   │   ├── models
│   │   │       │   │   │   │   ├── input-schemas.model.ts
│   │   │       │   │   │   │   └── schema-helpers.ts
│   │   │       │   │   │   ├── utils
│   │   │       │   │   │   │   ├── component-validation.ts
│   │   │       │   │   │   │   ├── cross-platform-path.ts
│   │   │       │   │   │   │   ├── handler-helpers.ts
│   │   │       │   │   │   │   ├── output.utils.ts
│   │   │       │   │   │   │   └── regex-helpers.ts
│   │   │       │   │   │   └── violation-analysis
│   │   │       │   │   │       ├── base-analyzer.ts
│   │   │       │   │   │       ├── coverage-analyzer.ts
│   │   │       │   │   │       ├── formatters.ts
│   │   │       │   │   │       ├── index.ts
│   │   │       │   │   │       └── types.ts
│   │   │       │   │   └── tools.ts
│   │   │       │   ├── schema.ts
│   │   │       │   ├── tools.ts
│   │   │       │   ├── types.ts
│   │   │       │   └── utils.ts
│   │   │       └── validation
│   │   │           ├── angular-mcp-server-options.schema.ts
│   │   │           ├── ds-components-file-loader.validation.ts
│   │   │           ├── ds-components-file.validation.ts
│   │   │           ├── ds-components.schema.ts
│   │   │           └── file-existence.ts
│   │   ├── tsconfig.json
│   │   ├── tsconfig.lib.json
│   │   ├── tsconfig.tsbuildinfo
│   │   └── vitest.config.mts
│   ├── minimal-repo
│   │   └── packages
│   │       ├── application
│   │       │   ├── angular.json
│   │       │   ├── code-pushup.config.ts
│   │       │   ├── src
│   │       │   │   ├── app
│   │       │   │   │   ├── app.component.ts
│   │       │   │   │   ├── app.config.ts
│   │       │   │   │   ├── app.routes.ts
│   │       │   │   │   ├── components
│   │       │   │   │   │   ├── refactoring-tests
│   │       │   │   │   │   │   ├── bad-alert-tooltip-input.component.ts
│   │       │   │   │   │   │   ├── bad-alert.component.ts
│   │       │   │   │   │   │   ├── bad-button-dropdown.component.ts
│   │       │   │   │   │   │   ├── bad-document.component.ts
│   │       │   │   │   │   │   ├── bad-global-this.component.ts
│   │       │   │   │   │   │   ├── bad-mixed-external-assets.component.css
│   │       │   │   │   │   │   ├── bad-mixed-external-assets.component.html
│   │       │   │   │   │   │   ├── bad-mixed-external-assets.component.ts
│   │       │   │   │   │   │   ├── bad-mixed-not-standalone.component.ts
│   │       │   │   │   │   │   ├── bad-mixed.component.ts
│   │       │   │   │   │   │   ├── bad-mixed.module.ts
│   │       │   │   │   │   │   ├── bad-modal-progress.component.ts
│   │       │   │   │   │   │   ├── bad-this-window-document.component.ts
│   │       │   │   │   │   │   ├── bad-window.component.ts
│   │       │   │   │   │   │   ├── complex-components
│   │       │   │   │   │   │   │   ├── first-case
│   │       │   │   │   │   │   │   │   ├── dashboard-demo.component.html
│   │       │   │   │   │   │   │   │   ├── dashboard-demo.component.scss
│   │       │   │   │   │   │   │   │   ├── dashboard-demo.component.ts
│   │       │   │   │   │   │   │   │   ├── dashboard-header.component.html
│   │       │   │   │   │   │   │   │   ├── dashboard-header.component.scss
│   │       │   │   │   │   │   │   │   └── dashboard-header.component.ts
│   │       │   │   │   │   │   │   ├── second-case
│   │       │   │   │   │   │   │   │   ├── complex-badge-widget.component.scss
│   │       │   │   │   │   │   │   │   ├── complex-badge-widget.component.ts
│   │       │   │   │   │   │   │   │   └── complex-widget-demo.component.ts
│   │       │   │   │   │   │   │   └── third-case
│   │       │   │   │   │   │   │       ├── product-card.component.scss
│   │       │   │   │   │   │   │       ├── product-card.component.ts
│   │       │   │   │   │   │   │       └── product-showcase.component.ts
│   │       │   │   │   │   │   ├── group-1
│   │       │   │   │   │   │   │   ├── bad-mixed-1.component.ts
│   │       │   │   │   │   │   │   ├── bad-mixed-1.module.ts
│   │       │   │   │   │   │   │   ├── bad-mixed-external-assets-1.component.css
│   │       │   │   │   │   │   │   ├── bad-mixed-external-assets-1.component.html
│   │       │   │   │   │   │   │   ├── bad-mixed-external-assets-1.component.ts
│   │       │   │   │   │   │   │   └── bad-mixed-not-standalone-1.component.ts
│   │       │   │   │   │   │   ├── group-2
│   │       │   │   │   │   │   │   ├── bad-mixed-2.component.ts
│   │       │   │   │   │   │   │   ├── bad-mixed-2.module.ts
│   │       │   │   │   │   │   │   ├── bad-mixed-external-assets-2.component.css
│   │       │   │   │   │   │   │   ├── bad-mixed-external-assets-2.component.html
│   │       │   │   │   │   │   │   ├── bad-mixed-external-assets-2.component.ts
│   │       │   │   │   │   │   │   └── bad-mixed-not-standalone-2.component.ts
│   │       │   │   │   │   │   ├── group-3
│   │       │   │   │   │   │   │   ├── bad-mixed-3.component.spec.ts
│   │       │   │   │   │   │   │   ├── bad-mixed-3.component.ts
│   │       │   │   │   │   │   │   ├── bad-mixed-3.module.ts
│   │       │   │   │   │   │   │   ├── bad-mixed-external-assets-3.component.css
│   │       │   │   │   │   │   │   ├── bad-mixed-external-assets-3.component.html
│   │       │   │   │   │   │   │   ├── bad-mixed-external-assets-3.component.ts
│   │       │   │   │   │   │   │   ├── bad-mixed-not-standalone-3.component.ts
│   │       │   │   │   │   │   │   └── lazy-loader-3.component.ts
│   │       │   │   │   │   │   └── group-4
│   │       │   │   │   │   │       ├── multi-violation-test.component.html
│   │       │   │   │   │   │       ├── multi-violation-test.component.scss
│   │       │   │   │   │   │       └── multi-violation-test.component.ts
│   │       │   │   │   │   └── validation-tests
│   │       │   │   │   │       ├── circular-dependency.component.ts
│   │       │   │   │   │       ├── external-files-missing.component.ts
│   │       │   │   │   │       ├── invalid-lifecycle.component.ts
│   │       │   │   │   │       ├── invalid-pipe-usage.component.ts
│   │       │   │   │   │       ├── invalid-template-syntax.component.ts
│   │       │   │   │   │       ├── missing-imports.component.ts
│   │       │   │   │   │       ├── missing-method.component.ts
│   │       │   │   │   │       ├── README.md
│   │       │   │   │   │       ├── standalone-module-conflict.component.ts
│   │       │   │   │   │       ├── standalone-module-conflict.module.ts
│   │       │   │   │   │       ├── template-reference-error.component.ts
│   │       │   │   │   │       ├── type-mismatch.component.ts
│   │       │   │   │   │       ├── valid.component.ts
│   │       │   │   │   │       ├── wrong-decorator-usage.component.ts
│   │       │   │   │   │       └── wrong-property-binding.component.ts
│   │       │   │   │   └── styles
│   │       │   │   │       ├── bad-global-styles.scss
│   │       │   │   │       ├── base
│   │       │   │   │       │   ├── _reset.scss
│   │       │   │   │       │   └── base.scss
│   │       │   │   │       ├── components
│   │       │   │   │       │   └── components.scss
│   │       │   │   │       ├── extended-deprecated-styles.scss
│   │       │   │   │       ├── layout
│   │       │   │   │       │   └── layout.scss
│   │       │   │   │       ├── new-styles-1.scss
│   │       │   │   │       ├── new-styles-10.scss
│   │       │   │   │       ├── new-styles-2.scss
│   │       │   │   │       ├── new-styles-3.scss
│   │       │   │   │       ├── new-styles-4.scss
│   │       │   │   │       ├── new-styles-5.scss
│   │       │   │   │       ├── new-styles-6.scss
│   │       │   │   │       ├── new-styles-7.scss
│   │       │   │   │       ├── new-styles-8.scss
│   │       │   │   │       ├── new-styles-9.scss
│   │       │   │   │       ├── themes
│   │       │   │   │       │   └── themes.scss
│   │       │   │   │       └── utilities
│   │       │   │   │           └── utilities.scss
│   │       │   │   ├── index.html
│   │       │   │   ├── main.ts
│   │       │   │   └── styles.css
│   │       │   ├── tsconfig.app.json
│   │       │   ├── tsconfig.json
│   │       │   └── tsconfig.spec.json
│   │       └── design-system
│   │           ├── component-options.mjs
│   │           ├── storybook
│   │           │   └── card
│   │           │       └── card-tabs
│   │           │           └── overview.mdx
│   │           ├── storybook-host-app
│   │           │   └── src
│   │           │       └── components
│   │           │           ├── badge
│   │           │           │   ├── badge-tabs
│   │           │           │   │   ├── api.mdx
│   │           │           │   │   ├── examples.mdx
│   │           │           │   │   └── overview.mdx
│   │           │           │   ├── badge.component.mdx
│   │           │           │   └── badge.component.stories.ts
│   │           │           ├── modal
│   │           │           │   ├── demo-cdk-dialog-cmp.component.ts
│   │           │           │   ├── demo-modal-cmp.component.ts
│   │           │           │   ├── modal-tabs
│   │           │           │   │   ├── api.mdx
│   │           │           │   │   ├── examples.mdx
│   │           │           │   │   └── overview.mdx
│   │           │           │   ├── modal.component.mdx
│   │           │           │   └── modal.component.stories.ts
│   │           │           └── segmented-control
│   │           │               ├── segmented-control-tabs
│   │           │               │   ├── api.mdx
│   │           │               │   ├── examples.mdx
│   │           │               │   └── overview.mdx
│   │           │               ├── segmented-control.component.mdx
│   │           │               └── segmented-control.component.stories.ts
│   │           └── ui
│   │               ├── badge
│   │               │   ├── package.json
│   │               │   ├── project.json
│   │               │   └── src
│   │               │       └── badge.component.ts
│   │               ├── modal
│   │               │   ├── package.json
│   │               │   ├── project.json
│   │               │   └── src
│   │               │       ├── modal-content.component.ts
│   │               │       ├── modal-header
│   │               │       │   └── modal-header.component.ts
│   │               │       ├── modal-header-drag
│   │               │       │   └── modal-header-drag.component.ts
│   │               │       └── modal.component.ts
│   │               ├── rx-host-listener
│   │               │   ├── package.json
│   │               │   ├── project.json
│   │               │   └── src
│   │               │       └── rx-host-listener.ts
│   │               └── segmented-control
│   │                   ├── package.json
│   │                   ├── project.json
│   │                   └── src
│   │                       ├── segmented-control.component.html
│   │                       ├── segmented-control.component.ts
│   │                       ├── segmented-control.token.ts
│   │                       └── segmented-option.component.ts
│   └── shared
│       ├── angular-ast-utils
│       │   ├── .spec.swcrc
│       │   ├── ai
│       │   │   ├── API.md
│       │   │   ├── EXAMPLES.md
│       │   │   └── FUNCTIONS.md
│       │   ├── docs
│       │   │   └── angular-component-tree.md
│       │   ├── eslint.config.mjs
│       │   ├── jest.config.ts
│       │   ├── package.json
│       │   ├── README.md
│       │   ├── src
│       │   │   ├── index.ts
│       │   │   └── lib
│       │   │       ├── constants.ts
│       │   │       ├── decorator-config.visitor.inline-styles.spec.ts
│       │   │       ├── decorator-config.visitor.spec.ts
│       │   │       ├── decorator-config.visitor.ts
│       │   │       ├── parse-component.ts
│       │   │       ├── schema.ts
│       │   │       ├── styles
│       │   │       │   └── utils.ts
│       │   │       ├── template
│       │   │       │   ├── noop-tmpl-visitor.ts
│       │   │       │   ├── template.walk.ts
│       │   │       │   ├── utils.spec.ts
│       │   │       │   ├── utils.ts
│       │   │       │   └── utils.unit.test.ts
│       │   │       ├── ts.walk.ts
│       │   │       ├── types.ts
│       │   │       └── utils.ts
│       │   ├── tsconfig.json
│       │   ├── tsconfig.lib.json
│       │   ├── tsconfig.spec.json
│       │   └── vitest.config.mts
│       ├── DEPENDENCIES.md
│       ├── ds-component-coverage
│       │   ├── .spec.swcrc
│       │   ├── ai
│       │   │   ├── API.md
│       │   │   ├── EXAMPLES.md
│       │   │   └── FUNCTIONS.md
│       │   ├── docs
│       │   │   ├── examples
│       │   │   │   ├── report.json
│       │   │   │   └── report.md
│       │   │   ├── images
│       │   │   │   └── report-overview.png
│       │   │   └── README.md
│       │   ├── jest.config.ts
│       │   ├── mocks
│       │   │   └── fixtures
│       │   │       └── e2e
│       │   │           ├── asset-location
│       │   │           │   ├── code-pushup.config.ts
│       │   │           │   ├── inl-styl-inl-tmpl
│       │   │           │   │   └── inl-styl-inl-tmpl.component.ts
│       │   │           │   ├── inl-styl-url-tmpl
│       │   │           │   │   ├── inl-styl-url-tmpl.component.html
│       │   │           │   │   └── inl-styl-url-tmpl.component.ts
│       │   │           │   ├── multi-url-styl-inl-tmpl
│       │   │           │   │   ├── multi-url-styl-inl-tmpl-1.component.css
│       │   │           │   │   ├── multi-url-styl-inl-tmpl-2.component.css
│       │   │           │   │   └── multi-url-styl-inl-tmpl.component.ts
│       │   │           │   ├── url-styl-inl-tmpl
│       │   │           │   │   ├── url-styl-inl-tmpl.component.css
│       │   │           │   │   └── url-styl-inl-tmpl.component.ts
│       │   │           │   ├── url-styl-single-inl-tmpl
│       │   │           │   │   ├── url-styl-inl-tmpl.component.ts
│       │   │           │   │   └── url-styl-single-inl-tmpl.component.css
│       │   │           │   └── url-styl-url-tmpl
│       │   │           │       ├── inl-styl-url-tmpl.component.css
│       │   │           │       ├── inl-styl-url-tmpl.component.html
│       │   │           │       └── inl-styl-url-tmpl.component.ts
│       │   │           ├── demo
│       │   │           │   ├── code-pushup.config.ts
│       │   │           │   ├── prompt.md
│       │   │           │   └── src
│       │   │           │       ├── bad-button-dropdown.component.ts
│       │   │           │       ├── bad-modal-progress.component.ts
│       │   │           │       ├── mixed-external-assets.component.css
│       │   │           │       ├── mixed-external-assets.component.html
│       │   │           │       ├── mixed-external-assets.component.ts
│       │   │           │       └── sub-folder-1
│       │   │           │           ├── bad-alert.component.ts
│       │   │           │           ├── button.component.ts
│       │   │           │           └── sub-folder-2
│       │   │           │               ├── bad-alert-tooltip-input.component.ts
│       │   │           │               └── bad-mixed.component.ts
│       │   │           ├── line-number
│       │   │           │   ├── code-pushup.config.ts
│       │   │           │   ├── inl-styl-single.component.ts
│       │   │           │   ├── inl-styl-span.component.ts
│       │   │           │   ├── inl-tmpl-single.component.ts
│       │   │           │   ├── inl-tmpl-span.component.ts
│       │   │           │   ├── url-style
│       │   │           │   │   ├── url-styl-single.component.css
│       │   │           │   │   ├── url-styl-single.component.ts
│       │   │           │   │   ├── url-styl-span.component.css
│       │   │           │   │   └── url-styl-span.component.ts
│       │   │           │   └── url-tmpl
│       │   │           │       ├── url-tmpl-single.component.html
│       │   │           │       ├── url-tmpl-single.component.ts
│       │   │           │       ├── url-tmpl-span.component.html
│       │   │           │       └── url-tmpl-span.component.ts
│       │   │           ├── style-format
│       │   │           │   ├── code-pushup.config.ts
│       │   │           │   ├── inl-css.component.ts
│       │   │           │   ├── inl-scss.component.ts
│       │   │           │   ├── styles.css
│       │   │           │   ├── styles.scss
│       │   │           │   ├── url-css.component.ts
│       │   │           │   └── url-scss.component.ts
│       │   │           └── template-syntax
│       │   │               ├── class-attribute.component.ts
│       │   │               ├── class-binding.component.ts
│       │   │               ├── code-pushup.config.ts
│       │   │               └── ng-class-binding.component.ts
│       │   ├── package.json
│       │   ├── src
│       │   │   ├── core.config.ts
│       │   │   ├── index.ts
│       │   │   └── lib
│       │   │       ├── constants.ts
│       │   │       ├── ds-component-coverage.plugin.ts
│       │   │       ├── runner
│       │   │       │   ├── audits
│       │   │       │   │   └── ds-coverage
│       │   │       │   │       ├── class-definition.utils.ts
│       │   │       │   │       ├── class-definition.visitor.ts
│       │   │       │   │       ├── class-definition.visitor.unit.test.ts
│       │   │       │   │       ├── class-usage.utils.ts
│       │   │       │   │       ├── class-usage.visitor.spec.ts
│       │   │       │   │       ├── class-usage.visitor.ts
│       │   │       │   │       ├── constants.ts
│       │   │       │   │       ├── ds-coverage.audit.ts
│       │   │       │   │       ├── schema.ts
│       │   │       │   │       └── utils.ts
│       │   │       │   ├── create-runner.ts
│       │   │       │   └── schema.ts
│       │   │       └── utils.ts
│       │   ├── tsconfig.json
│       │   ├── tsconfig.lib.json
│       │   ├── tsconfig.spec.json
│       │   └── vitest.config.mts
│       ├── LLMS.md
│       ├── models
│       │   ├── ai
│       │   │   ├── API.md
│       │   │   ├── EXAMPLES.md
│       │   │   └── FUNCTIONS.md
│       │   ├── package.json
│       │   ├── README.md
│       │   ├── src
│       │   │   ├── index.ts
│       │   │   └── lib
│       │   │       ├── cli.ts
│       │   │       ├── diagnostics.ts
│       │   │       └── mcp.ts
│       │   ├── tsconfig.json
│       │   └── tsconfig.lib.json
│       ├── styles-ast-utils
│       │   ├── .spec.swcrc
│       │   ├── ai
│       │   │   ├── API.md
│       │   │   ├── EXAMPLES.md
│       │   │   └── FUNCTIONS.md
│       │   ├── jest.config.ts
│       │   ├── package.json
│       │   ├── README.md
│       │   ├── src
│       │   │   ├── index.ts
│       │   │   └── lib
│       │   │       ├── postcss-safe-parser.d.ts
│       │   │       ├── styles-ast-utils.spec.ts
│       │   │       ├── styles-ast-utils.ts
│       │   │       ├── stylesheet.parse.ts
│       │   │       ├── stylesheet.parse.unit.test.ts
│       │   │       ├── stylesheet.visitor.ts
│       │   │       ├── stylesheet.walk.ts
│       │   │       ├── types.ts
│       │   │       ├── utils.ts
│       │   │       └── utils.unit.test.ts
│       │   ├── tsconfig.json
│       │   ├── tsconfig.lib.json
│       │   ├── tsconfig.spec.json
│       │   └── vitest.config.mts
│       ├── typescript-ast-utils
│       │   ├── .spec.swcrc
│       │   ├── ai
│       │   │   ├── API.md
│       │   │   ├── EXAMPLES.md
│       │   │   └── FUNCTIONS.md
│       │   ├── jest.config.ts
│       │   ├── package.json
│       │   ├── README.md
│       │   ├── src
│       │   │   ├── index.ts
│       │   │   └── lib
│       │   │       ├── constants.ts
│       │   │       └── utils.ts
│       │   ├── tsconfig.json
│       │   ├── tsconfig.lib.json
│       │   ├── tsconfig.spec.json
│       │   └── vitest.config.mts
│       └── utils
│           ├── .spec.swcrc
│           ├── ai
│           │   ├── API.md
│           │   ├── EXAMPLES.md
│           │   └── FUNCTIONS.md
│           ├── package.json
│           ├── README.md
│           ├── src
│           │   ├── index.ts
│           │   └── lib
│           │       ├── execute-process.ts
│           │       ├── execute-process.unit.test.ts
│           │       ├── file
│           │       │   ├── default-export-loader.spec.ts
│           │       │   ├── default-export-loader.ts
│           │       │   ├── file.resolver.ts
│           │       │   └── find-in-file.ts
│           │       ├── format-command-log.integration.test.ts
│           │       ├── format-command-log.ts
│           │       ├── logging.ts
│           │       └── utils.ts
│           ├── tsconfig.json
│           ├── tsconfig.lib.json
│           ├── tsconfig.spec.json
│           ├── vite.config.ts
│           └── vitest.config.mts
├── README.md
├── testing
│   ├── setup
│   │   ├── eslint.config.mjs
│   │   ├── eslint.next.config.mjs
│   │   ├── package.json
│   │   ├── README.md
│   │   ├── src
│   │   │   ├── index.d.ts
│   │   │   ├── index.mjs
│   │   │   └── memfs.constants.ts
│   │   ├── tsconfig.json
│   │   ├── tsconfig.lib.json
│   │   ├── tsconfig.spec.json
│   │   ├── vitest.config.mts
│   │   └── vitest.integration.config.mts
│   ├── utils
│   │   ├── eslint.config.mjs
│   │   ├── eslint.next.config.mjs
│   │   ├── package.json
│   │   ├── README.md
│   │   ├── src
│   │   │   ├── index.ts
│   │   │   └── lib
│   │   │       ├── constants.ts
│   │   │       ├── e2e-setup.ts
│   │   │       ├── execute-process-helper.mock.ts
│   │   │       ├── execute-process.mock.mjs
│   │   │       ├── os-agnostic-paths.ts
│   │   │       ├── os-agnostic-paths.unit.test.ts
│   │   │       ├── source-file-from.code.ts
│   │   │       └── string.ts
│   │   ├── tsconfig.json
│   │   ├── tsconfig.lib.json
│   │   ├── tsconfig.spec.json
│   │   ├── vite.config.ts
│   │   ├── vitest.config.mts
│   │   └── vitest.integration.config.mts
│   └── vitest-setup
│       ├── eslint.config.mjs
│       ├── eslint.next.config.mjs
│       ├── package.json
│       ├── README.md
│       ├── src
│       │   ├── index.ts
│       │   └── lib
│       │       ├── configuration.ts
│       │       └── fs-memfs.setup-file.ts
│       ├── tsconfig.json
│       ├── tsconfig.lib.json
│       ├── tsconfig.spec.json
│       ├── vite.config.ts
│       ├── vitest.config.mts
│       └── vitest.integration.config.mts
├── tools
│   ├── nx-advanced-profile.bin.js
│   ├── nx-advanced-profile.js
│   ├── nx-advanced-profile.postinstall.js
│   └── perf_hooks.patch.js
├── tsconfig.base.json
├── tsconfig.json
└── vitest.workspace.ts
```

# Files

--------------------------------------------------------------------------------
/packages/shared/ds-component-coverage/ai/FUNCTIONS.md:
--------------------------------------------------------------------------------

```markdown
# Public API — Quick Reference

| Symbol                                 | Kind     | Signature                                                                                     | Summary                                              |
| -------------------------------------- | -------- | --------------------------------------------------------------------------------------------- | ---------------------------------------------------- |
| `ANGULAR_DS_USAGE_PLUGIN_SLUG`         | constant | `const ANGULAR_DS_USAGE_PLUGIN_SLUG: string`                                                  | Plugin slug identifier for ds-component-coverage     |
| `ComponentCoverageRunnerOptionsSchema` | schema   | `const ComponentCoverageRunnerOptionsSchema: ZodObject`                                       | Zod schema for runner configuration validation       |
| `ComponentReplacement`                 | type     | `type ComponentReplacement`                                                                   | Type for component replacement configuration         |
| `ComponentReplacementSchema`           | schema   | `const ComponentReplacementSchema: ZodObject`                                                 | Zod schema for component replacement validation      |
| `CreateRunnerConfig`                   | type     | `type CreateRunnerConfig`                                                                     | Type alias for runner configuration                  |
| `dsComponentCoveragePlugin`            | function | `dsComponentCoveragePlugin(options: DsComponentUsagePluginConfig): PluginConfig`              | Create DS component coverage plugin for Code Pushup  |
| `DsComponentUsagePluginConfig`         | type     | `type DsComponentUsagePluginConfig`                                                           | Configuration type for the DS component usage plugin |
| `getAngularDsUsageCategoryRefs`        | function | `getAngularDsUsageCategoryRefs(componentReplacements: ComponentReplacement[]): CategoryRef[]` | Generate category references for audit organization  |
| `runnerFunction`                       | function | `runnerFunction(config: CreateRunnerConfig): Promise<AuditOutputs>`                           | Execute DS component coverage analysis               |

```

--------------------------------------------------------------------------------
/packages/minimal-repo/packages/design-system/storybook-host-app/src/components/badge/badge-tabs/api.mdx:
--------------------------------------------------------------------------------

```markdown
## Inputs

| Name       | Type                                                                   | Default     | Description                                            |
| ---------- | ---------------------------------------------------------------------- | ----------- | ------------------------------------------------------ |
| `size`     | `'xsmall'` \| `'medium'`                                               | `'medium'`  | Controls the badge size.                               |
| `variant`  | A value from `DsBadgeVariant`<br/>(e.g. `'primary'`, `'green-strong'`) | `'primary'` | Visual style variant (color + intensity).              |
| `disabled` | `boolean`                                                              | `false`     | Visually and semantically marks the badge as disabled. |
| `inverse`  | `boolean`                                                              | `false`     | Applies inverse theme styling (for dark backgrounds).  |

---

## Outputs / Events

This component does not emit any custom events.

---

## Content Projection

The badge supports slot-based content for flexible icon/text layouts.

### Named Slots

- `[slot=start]` – content rendered before the main text.
- `[slot=end]` – content rendered after the main text.

### Default Slot

Text or elements directly inside the component will be rendered in the central label span.

```html
<ds-badge variant="green-strong">
  <span slot="start">✔</span>
  Confirmed
  <span slot="end">✓</span>
</ds-badge>
```

---

## Host Element Behavior

The following CSS classes are dynamically applied to the host element to reflect component state:

- `ds-badge` – base class applied to all badge instances
- `ds-badge-xsmall` or `ds-badge-medium` – based on the `size` input
- `ds-badge-[variant]` – where `[variant]` corresponds to the selected variant (e.g. `ds-badge-green-subtle`)
- `ds-badge-inverse` – applied when `inverse` is `true`
- `ds-badge-disabled` – applied when `disabled` is `true`

These classes are computed and set via the `hostClass()` method using Angular’s `@computed()` signal.

The host also defines the following attributes:

- `role="img"` – applied for accessibility support
- `aria-label` – dynamically generated from content, prepended with `"Disabled badge:"` if `disabled` is `true`

```

--------------------------------------------------------------------------------
/packages/shared/angular-ast-utils/src/lib/utils.ts:
--------------------------------------------------------------------------------

```typescript
import * as ts from 'typescript';
import { removeQuotes } from '@push-based/typescript-ast-utils';
import { AngularUnit, Asset } from './types.js';

export function assetFromPropertyValueInitializer<T>({
  prop,
  sourceFile,
  textParser,
}: {
  prop: ts.PropertyAssignment;
  sourceFile: ts.SourceFile;
  textParser: (text: string) => Promise<T>;
}): Asset<T> {
  const { line: startLine } = sourceFile.getLineAndCharacterOfPosition(
    prop.getStart(sourceFile),
  );
  const value = removeQuotes(prop.initializer, sourceFile);
  return {
    filePath: sourceFile.fileName,
    startLine,
    parse: () => textParser(value),
  } satisfies Asset<T>;
}

export function assetFromPropertyArrayInitializer<T>(
  prop: ts.PropertyAssignment,
  sourceFile: ts.SourceFile,
  textParser: (text: string) => Promise<T>,
): Asset<T>[] {
  const elements: ts.NodeArray<ts.Expression> = ts.isArrayLiteralExpression(
    prop.initializer,
  )
    ? prop.initializer.elements
    : ts.factory.createNodeArray();

  return elements.map((element) => {
    const { line: startLine } = sourceFile.getLineAndCharacterOfPosition(
      element.getStart(sourceFile),
    );
    const value = removeQuotes(element, sourceFile);
    return {
      filePath: sourceFile.fileName,
      startLine,
      parse: () => textParser(value),
    } satisfies Asset<T>;
  });
}

import { findFilesWithPattern } from '@push-based/utils';
import { parseComponents } from './parse-component.js';

const unitToSearchPattern = {
  component: '@Component',
  directive: '@Directive',
  pipe: '@Pipe',
  service: '@Service',
} as const satisfies Record<AngularUnit, string>;

export async function findAngularUnits(
  directory: string,
  unit: AngularUnit,
): Promise<string[]> {
  const searchPattern =
    unitToSearchPattern[unit] ?? unitToSearchPattern.component;
  return await findFilesWithPattern(directory, searchPattern);
}

/**
 * Parse Angular units in a given directory.
 *
 * @param directory
 * @param unit
 */
export async function parseAngularUnit(directory: string, unit: AngularUnit) {
  const componentFiles = await findAngularUnits(directory, unit);

  switch (unit) {
    case 'component':
      return parseComponents(componentFiles);
    default:
      throw new Error(`Unit ${unit} is not supported for parsing.`);
  }
}

```

--------------------------------------------------------------------------------
/packages/angular-mcp-server/src/lib/tools/ds/component-usage-graph/build-component-usage-graph.tool.ts:
--------------------------------------------------------------------------------

```typescript
import {
  createHandler,
  BaseHandlerOptions,
} from '../shared/utils/handler-helpers.js';
import {
  buildComponentUsageGraph,
  clearAnalysisCache,
} from './utils/component-usage-graph-builder.js';
import { filterGraph, printComponents } from './utils/component-helpers.js';
import { buildComponentUsageGraphSchema } from './models/schema.js';
import { resolveCrossPlatformPath } from '../shared/utils/cross-platform-path.js';

interface ComponentUsageGraphOptions extends BaseHandlerOptions {
  directory: string;
  violationFiles: string[];
}

export const buildComponentUsageGraphHandler = createHandler<
  ComponentUsageGraphOptions,
  any
>(
  buildComponentUsageGraphSchema.name,
  async (params, { cwd, workspaceRoot }) => {
    const startTime = performance.now();

    try {
      const { directory, violationFiles } = params;

      if (
        !violationFiles ||
        !Array.isArray(violationFiles) ||
        violationFiles.length === 0
      ) {
        throw new Error(
          'violationFiles parameter is required and must be an array of strings',
        );
      }

      const fullComponentUsageGraph = await buildComponentUsageGraph({
        cwd,
        directory,
        workspaceRoot,
      });

      const targetPath = resolveCrossPlatformPath(cwd, directory);

      const componentUsageGraph =
        violationFiles.length > 0
          ? filterGraph(fullComponentUsageGraph, violationFiles, targetPath)
          : fullComponentUsageGraph;

      const content = printComponents(componentUsageGraph, 'entity');
      const totalTime = performance.now() - startTime;

      return {
        content,
        timing: `⚡ Analysis completed in ${totalTime.toFixed(2)}ms (${Object.keys(fullComponentUsageGraph).length} files processed)`,
      };
    } finally {
      clearAnalysisCache();
    }
  },
  (result) => {
    // Format the result as text lines
    const lines: string[] = [];

    if (Array.isArray(result.content)) {
      result.content.forEach((item: any) => {
        if (item.type === 'text') {
          lines.push(item.text);
        } else {
          lines.push(JSON.stringify(item));
        }
      });
    }

    lines.push(result.timing);
    return lines;
  },
);

export const buildComponentUsageGraphTools = [
  {
    schema: buildComponentUsageGraphSchema,
    handler: buildComponentUsageGraphHandler,
  },
];

```

--------------------------------------------------------------------------------
/packages/shared/angular-ast-utils/src/lib/template/utils.unit.test.ts:
--------------------------------------------------------------------------------

```typescript
import { describe, expect } from 'vitest';
import { tmplAstElementToSource } from './utils';
import type { TmplAstElement } from '@angular/compiler' with { 'resolution-mode': 'import' };

describe('tmplAstElementToSource', () => {
  let parseTemplate: typeof import('@angular/compiler').parseTemplate;
  beforeAll(async () => {
    parseTemplate = (await import('@angular/compiler')).parseTemplate;
  });
  it('should have line number starting from 1', () => {
    const result = parseTemplate(
      `<button class="btn">click</button>`,
      'inline-template.component.ts',
    );
    const attribute = result.nodes.at(0) as TmplAstElement;
    const source = tmplAstElementToSource(attribute);
    expect(source).toStrictEqual({
      file: 'inline-template.component.ts',
      position: {
        startLine: 1,
      },
    });
  });

  it('should have line number where startLine is respected', () => {
    const result = parseTemplate(
      `<button class="btn">click</button>`,
      'template.html',
    );
    const attribute = (result.nodes.at(0) as TmplAstElement)?.attributes.at(0);

    const source = tmplAstElementToSource(attribute);

    expect(source).toStrictEqual({
      file: expect.stringMatching(/template\.html$/),
      position: {
        startLine: 5,
        startColumn: 1,
        endLine: 5,
        endColumn: 19,
      },
    });
  });

  it('should have correct line number for starting line breaks', () => {
    const result = parseTemplate(
      `

<button class="btn">click</button>`,
      'template.html',
    );
    const attribute = (result.nodes.at(0) as TmplAstElement)?.attributes.at(0);
    const source = tmplAstElementToSource(attribute);

    expect(source).toStrictEqual({
      file: expect.stringMatching(/template\.html/),
      position: {
        startLine: 3,
        startColumn: 1,
        endLine: 3,
        endColumn: 19,
      },
    });
  });

  it('should have correct line number for spans', () => {
    const result = parseTemplate(
      `<button class="btn">
  click
</button>`,
      'template.html',
    );
    const attribute = result.nodes.at(0)?.attributes.at(0);
    const source = tmplAstElementToSource(attribute);

    expect(source).toStrictEqual({
      file: expect.stringMatching(/template\.html$/),
      position: {
        startLine: 1,
        startColumn: 1,
        endLine: 4,
        endColumn: 1,
      },
    });
  });
});

```

--------------------------------------------------------------------------------
/packages/shared/utils/ai/API.md:
--------------------------------------------------------------------------------

```markdown
# Utils

Comprehensive **utility library** providing process execution, file operations, string manipulation, and logging utilities for Node.js applications.

## Minimal usage

```ts
import {
  executeProcess,
  findFilesWithPattern,
  resolveFileCached,
  loadDefaultExport,
  objectToCliArgs,
} from '@push-based/utils';

import { slugify } from '@code-pushup/utils';

// Execute a process with observer
const result = await executeProcess({
  command: 'node',
  args: ['--version'],
  observer: {
    onStdout: (data) => console.log(data),
  },
});

// Find files containing a pattern
const files = await findFilesWithPattern('./src', 'Component');

// Resolve file with caching
const content = await resolveFileCached('./config.json');

// Load ES module default export
const config = await loadDefaultExport('./config.mjs');

// String utilities
const slug = slugify('Hello World!'); // → 'hello-world'
const args = objectToCliArgs({ name: 'test', verbose: true }); // → ['--name="test"', '--verbose']
```

## Key Features

- **Process Execution**: Robust child process management with observers and error handling
- **File Operations**: Cached file resolution and pattern-based file searching
- **ES Module Loading**: Dynamic import of ES modules with default export extraction
- **String Utilities**: Text transformation, slugification, and pluralization
- **CLI Utilities**: Object-to-arguments conversion and command formatting
- **Logging**: Environment-based verbose logging control
- **Type Safety**: Full TypeScript support with comprehensive type definitions

## Use Cases

- **Build Tools**: Execute CLI commands with real-time output monitoring
- **File Processing**: Search and resolve files efficiently with caching
- **Module Loading**: Dynamic import of configuration files and plugins
- **Code Generation**: Transform data into CLI arguments and formatted strings
- **Development Tools**: Create development utilities with proper logging
- **Static Analysis**: Find and process files based on content patterns
- **Cross-Platform**: Handle path normalization and command execution

## Documentation map

| Doc                            | What you'll find                            |
| ------------------------------ | ------------------------------------------- |
| [FUNCTIONS.md](./FUNCTIONS.md) | A–Z quick reference for every public symbol |
| [EXAMPLES.md](./EXAMPLES.md)   | Runnable scenarios with expected output     |

```

--------------------------------------------------------------------------------
/packages/minimal-repo/packages/design-system/storybook-host-app/src/components/badge/badge-tabs/overview.mdx:
--------------------------------------------------------------------------------

```markdown
import { Canvas } from '@storybook/blocks';

import * as BadgeStories from '../badge.component.stories';

The `DsBadge` component provides a way to display badges with various sizes, variants and additional features such as icons or placeholders (like success icon in our case).
You can make icon movable with properties slot for start and end position.

<Canvas of={BadgeStories.Default} />

---

## Usage

Import `DsBadge` in your component and apply `ds-badge` selector in your template.

```ts
import { DsBadge } from '@frontend/ui/badge'; // 👈 add to file imports

@Component({
  imports: [DsBadge], // 👈 add to component imports
  template: `...`,
})
export class AppComponent {}
```

---

## Badge variants

- By default, badges don't have any icons attached and are set in medium size and in primary variant
- Badges with icon are available only in xsmall and medium sizes
- Badges with icon are available only in xsmall and medium sizes
- Badges with icon are available only in medium sizes
- Badges with success icon are available only in xsmall and medium sizes
- Badges with both icon and success icon are available only in xsmall and medium sizes

---

## Accessibility

- The host sets `role="img"` for screen reader compatibility (interim solution until NVDA-compatible alternative is found).
- The `aria-label` is automatically generated from the badge text content.
- If `disabled` is true, the label is prefixed with `"Disabled badge: "`.
- Otherwise, it's `"Badge: {text}"`.

---

## Test Coverage

The component is comprehensively tested using Angular CDK Testing and a custom `DsBadgeHarness`.

### Functional Behavior

- Retrieves and verifies label text content
- Filters badges by:
- `size` (e.g., `'xsmall'`, `'medium'`)
- `variant` (e.g., `'secondary'`)
- label using a regex matcher
- Validates dynamic input changes:
- Updates to `size` correctly toggle size class
- Changes to `variant` are reflected in DOM state

### Slot Content

- Verifies rendering of text in the `[slot=start]` and `[slot=end]` containers
- Confirms SVG elements are supported and rendered in the start slot

### State & Styling

- Verifies toggling of the `inverse` class using `inverse` input
- Confirms `ds-badge-disabled` class presence when `disabled` is set

### Accessibility

- Checks that the computed `aria-label` accurately reflects the text and disabled state
- Validates screen reader output using `@guidepup/virtual-screen-reader`

```

--------------------------------------------------------------------------------
/packages/angular-mcp-server/src/lib/validation/ds-components-file-loader.validation.ts:
--------------------------------------------------------------------------------

```typescript
import * as fs from 'node:fs';
import * as path from 'node:path';
import {
  DsComponentsArraySchema,
  DsComponentSchema,
} from './ds-components.schema.js';
import { z } from 'zod';
import { loadDefaultExport } from '@push-based/utils';

export type DsComponent = z.infer<typeof DsComponentSchema>;
export type DsComponentsArray = z.infer<typeof DsComponentsArraySchema>;

export function validateDsComponent(rawComponent: unknown): DsComponent {
  const validation = DsComponentSchema.safeParse(rawComponent);
  if (!validation.success) {
    throw new Error(
      `Invalid component format: ${JSON.stringify(validation.error.format())}`,
    );
  }
  return validation.data;
}

export function validateDsComponentsArray(rawData: unknown): DsComponentsArray {
  if (!Array.isArray(rawData)) {
    throw new Error(`Expected array of components, received ${typeof rawData}`);
  }

  const validatedComponents: DsComponent[] = [];
  for (let i = 0; i < rawData.length; i++) {
    try {
      const validComponent = validateDsComponent(rawData[i]);
      validatedComponents.push(validComponent);
    } catch (ctx) {
      throw new Error(`Component at index ${i}: ${(ctx as Error).message}`);
    }
  }

  const arrayValidation =
    DsComponentsArraySchema.safeParse(validatedComponents);
  if (!arrayValidation.success) {
    throw new Error(
      `Array validation failed: ${JSON.stringify(
        arrayValidation.error.format(),
      )}`,
    );
  }

  return arrayValidation.data;
}

export async function loadAndValidateDsComponentsFile(
  cwd: string,
  deprecatedCssClassesPath: string,
): Promise<DsComponentsArray> {
  if (
    !deprecatedCssClassesPath ||
    typeof deprecatedCssClassesPath !== 'string'
  ) {
    throw new Error('deprecatedCssClassesPath must be a string path');
  }

  const absPath = path.resolve(cwd, deprecatedCssClassesPath);
  if (!fs.existsSync(absPath)) {
    throw new Error(`File not found at deprecatedCssClassesPath: ${absPath}`);
  }

  try {
    const rawData = await loadDefaultExport(absPath);

    return validateDsComponentsArray(rawData);
  } catch (ctx) {
    if (
      ctx instanceof Error &&
      (ctx.message.includes('Invalid component format') ||
        ctx.message.includes('Expected array of components') ||
        ctx.message.includes('Component at index'))
    ) {
      throw ctx;
    }
    throw new Error(
      `Failed to load configuration file: ${(ctx as Error).message}`,
    );
  }
}

```

--------------------------------------------------------------------------------
/packages/minimal-repo/packages/application/angular.json:
--------------------------------------------------------------------------------

```json
{
  "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
  "version": 1,
  "newProjectRoot": "projects",
  "projects": {
    "minimal": {
      "projectType": "application",
      "schematics": {},
      "root": "",
      "sourceRoot": "src",
      "prefix": "app",
      "architect": {
        "build": {
          "builder": "@angular-devkit/build-angular:application",
          "options": {
            "outputPath": "dist/minimal",
            "index": "src/index.html",
            "browser": "src/main.ts",
            "polyfills": ["zone.js"],
            "tsConfig": "tsconfig.app.json",
            "assets": [
              {
                "glob": "**/*",
                "input": "public"
              }
            ],
            "styles": ["src/styles.css"],
            "scripts": []
          },
          "configurations": {
            "production": {
              "budgets": [
                {
                  "type": "initial",
                  "maximumWarning": "500kB",
                  "maximumError": "1MB"
                },
                {
                  "type": "anyComponentStyle",
                  "maximumWarning": "4kB",
                  "maximumError": "8kB"
                }
              ],
              "outputHashing": "all"
            },
            "development": {
              "optimization": false,
              "extractLicenses": false,
              "sourceMap": true
            }
          },
          "defaultConfiguration": "production"
        },
        "serve": {
          "builder": "@angular-devkit/build-angular:dev-server",
          "configurations": {
            "production": {
              "buildTarget": "minimal:build:production"
            },
            "development": {
              "buildTarget": "minimal:build:development"
            }
          },
          "defaultConfiguration": "development"
        },
        "extract-i18n": {
          "builder": "@angular-devkit/build-angular:extract-i18n"
        },
        "test": {
          "builder": "@angular-devkit/build-angular:karma",
          "options": {
            "polyfills": ["zone.js", "zone.js/testing"],
            "tsConfig": "tsconfig.spec.json",
            "assets": [
              {
                "glob": "**/*",
                "input": "public"
              }
            ],
            "styles": ["src/styles.css"],
            "scripts": []
          }
        }
      }
    }
  }
}

```

--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------

```json
{
  "name": "@push-based/source",
  "version": "0.0.0",
  "type": "module",
  "license": "MIT",
  "scripts": {
    "publish:mcp": "nx build @push-based/angular-toolkit-mcp && cd packages/angular-mcp/dist && npm publish"
  },
  "private": true,
  "devDependencies": {
    "@eslint/js": "^9.28.0",
    "@modelcontextprotocol/inspector": "^0.14.0",
    "@nx/angular": "21.0.4",
    "@nx/eslint": "21.0.4",
    "@nx/eslint-plugin": "21.0.4",
    "@nx/express": "21.0.4",
    "@nx/jest": "21.0.4",
    "@nx/js": "21.0.4",
    "@nx/node": "21.0.4",
    "@nx/vite": "21.0.4",
    "@nx/web": "21.0.4",
    "@nx/webpack": "21.0.4",
    "@pmmmwh/react-refresh-webpack-plugin": "^0.5.16",
    "@svgr/webpack": "^8.0.1",
    "@swc-node/register": "~1.10.10",
    "@swc/cli": "0.7.7",
    "@swc/core": "~1.11.31",
    "@swc/helpers": "~0.5.11",
    "@swc/jest": "~0.2.38",
    "@types/express": "^4.17.23",
    "@types/jest": "^29.5.12",
    "@types/node": "~18.16.20",
    "@vitest/coverage-v8": "^3.2.3",
    "@vitest/ui": "^3.2.3",
    "eslint": "^9.28.0",
    "eslint-config-prettier": "10.1.5",
    "eslint-plugin-functional": "^9.0.2",
    "eslint-plugin-unicorn": "^59.0.1",
    "ignore-loader": "^0.1.2",
    "jest": "^29.7.0",
    "jest-environment-node": "^29.7.0",
    "jiti": "2.4.2",
    "jsdom": "~22.1.0",
    "nx": "21.0.4",
    "prettier": "^3.5.3",
    "react-refresh": "^0.17.0",
    "simple-git": "^3.28.0",
    "ts-jest": "^29.3.4",
    "ts-node": "10.9.2",
    "tslib": "^2.3.0",
    "typescript": "~5.7.2",
    "typescript-eslint": "^8.34.0",
    "vite": "^6.3.5",
    "vitest": "^3.2.3",
    "webpack-cli": "^6.0.1"
  },
  "workspaces": [
    "packages/*",
    "packages/server/app/*",
    "packages/libs/*",
    "packages/shared/*",
    "shared"
  ],
  "dependencies": {
    "@angular-devkit/schematics": "~19.2.0",
    "@angular/cli": "~19.2.0",
    "@angular/compiler": "~19.2.0",
    "@code-pushup/core": "^0.75.0",
    "@code-pushup/eslint-plugin": "^0.75.0",
    "@code-pushup/models": "^0.75.0",
    "@code-pushup/utils": "^0.75.0",
    "@modelcontextprotocol/sdk": "^1.12.1",
    "@push-based/ds-component-coverage": "^0.0.1",
    "@push-based/models": "^0.0.1",
    "@push-based/utils": "^0.0.1",
    "@vue/language-core": "^2.2.10",
    "axios": "^1.9.0",
    "express": "^4.21.2",
    "memfs": "^4.17.0",
    "microdiff": "^1.5.0",
    "postcss": "^8.5.4",
    "postcss-safe-parser": "^7.0.1",
    "simplegit": "^1.0.2",
    "ts-morph": "^26.0.0",
    "vite-plugin-dts": "^4.5.4",
    "zod": "^3.25.57"
  }
}

```

--------------------------------------------------------------------------------
/packages/angular-mcp-server/src/lib/tools/ds/component/utils/doc-helpers.ts:
--------------------------------------------------------------------------------

```typescript
import * as fs from 'fs';
import * as path from 'path';
import {
  validateComponentName,
  componentNameToTagName,
  componentNameToKebabCase,
} from '../../shared/utils/component-validation.js';
import { resolveCrossPlatformPath } from '../../shared/utils/cross-platform-path.js';

export interface ComponentDocPaths {
  componentName: string;
  folderSlug: string;
  tagName: string;
  paths: { api: string; overview: string };
}

export interface ComponentDocContent {
  componentName: string;
  tagName: string;
  api: string | null;
  overview: string | null;
}

export function getComponentDocPathsForName(
  docsBasePath: string,
  componentName: string,
): ComponentDocPaths {
  const folderSlug = componentNameToKebabCase(componentName);
  const tagName = componentNameToTagName(componentName);
  const base = path.join(docsBasePath, folderSlug);
  return {
    componentName,
    folderSlug,
    tagName,
    paths: {
      api: path.join(base, `${folderSlug}-tabs/api.mdx`),
      overview: path.join(base, `${folderSlug}-tabs/overview.mdx`),
    },
  };
}

export function enrichSingleComponentDoc(
  doc: ComponentDocPaths,
): ComponentDocContent {
  let apiContent = null;
  let overviewContent = null;

  if (fs.existsSync(doc.paths.api)) {
    apiContent = fs.readFileSync(doc.paths.api, 'utf-8');
  }
  if (fs.existsSync(doc.paths.overview)) {
    overviewContent = fs.readFileSync(doc.paths.overview, 'utf-8');
  }

  return {
    componentName: doc.componentName,
    tagName: doc.tagName,
    api: apiContent,
    overview: overviewContent,
  };
}

/**
 * Reusable helper function to get component documentation
 * @param componentName - The name of the component (e.g., DsButton)
 * @param storybookDocsRoot - The root path to the storybook docs
 * @param cwd - Current working directory (optional, defaults to process.cwd())
 * @returns Component documentation with API and Overview content
 * @throws Error if component validation fails or documentation retrieval fails
 */
export function getComponentDocs(
  componentName: string,
  storybookDocsRoot: string,
  cwd: string = process.cwd(),
): ComponentDocContent {
  try {
    validateComponentName(componentName);

    const docsBasePath = resolveCrossPlatformPath(cwd, storybookDocsRoot);
    const docPaths = getComponentDocPathsForName(docsBasePath, componentName);
    const doc = enrichSingleComponentDoc(docPaths);

    if (!doc || (!doc.api && !doc.overview)) {
      throw new Error(`No documentation found for component: ${componentName}`);
    }

    return doc;
  } catch (ctx) {
    throw new Error(
      `Error retrieving component documentation: ${(ctx as Error).message}`,
    );
  }
}

```

--------------------------------------------------------------------------------
/packages/minimal-repo/packages/design-system/storybook-host-app/src/components/segmented-control/segmented-control-tabs/overview.mdx:
--------------------------------------------------------------------------------

```markdown
import { Canvas } from '@storybook/blocks';

import * as SegmentedControlStories from '../segmented-control.component.stories';

The `DsSegmentedControlModule` is used for toggling between multiple options within a group. It enhances user experience by providing a visually distinct and easily navigable selection mechanism.

<Canvas of={SegmentedControlStories.Default} />

---

## Usage

Import `DsSegmentedControlModule` in your component, apply `ds-segmented-control` and `ds-segmented-options` selectors in your template.

```ts
import { DsSegmentedControlModule } from '@frontend/ui/segmented-control'; // 👈 add to file imports

@Component({
  imports: [DsSegmentedControlModule], // 👈 add to component imports
  template: `...`,
})
export class AppComponent {}
```

---

## Additional Configuration

### Segmented item width (max-width)

For text truncation we have token for min-width(44px) so it will apply the same for all the items but you need to specify the "max-width" for segmented item so that it will take the max width for the option and then it will truncate if it is more than that width.
You can customize the width of the segment item in the `segmented-control` using CSS variables. Set these variables in your CSS to adjust the width:

- `--ds-segment-item-text-max-width`: Sets the max-width of the segment item. Default is `auto`.

To adjust the width, add the following CSS to your styles:

```html
<ds-segmented-control class="ds-segmented-control component-class-name">
  <ds-segment-item>...</ds-segment-item>
</ds-segmented-control>
```

```css
.component-class-name {
  --ds-segment-item-text-max-width: 100px;
}
```

---

## Accessibility

- The `role` attribute on the segmented control is dynamically set to `tablist` or `radiogroup`.
- Each `ds-segmented-option`:
- Uses `role="tab"` or `role="radio"` depending on control type
- Has `aria-selected` or `aria-checked` to reflect selection state
- Includes `aria-label` using the option's `title` or `name`
- Uses `tabindex="0"` for selected, `-1` for others
- Supports keyboard navigation:
- `ArrowLeft` / `ArrowRight` for focus movement
- `Enter` / `Space` to activate
- Screen reader support confirmed via virtual-screen-reader tests

---

## Test Coverage

- Loads and renders segmented control and its options
- Selects options using:
- `selectTabByText`
- `selectTabByName`
- Supports toggling `fullWidth` and `inverse` inputs
- Switches between `tablist` and `radiogroup` roles
- Emits `activeOptionChange` when selection changes
- Keyboard navigation (arrow keys, enter, space)
- All roles and ARIA states validated for accessibility
- Screen reader flows tested using `@guidepup/virtual-screen-reader`

```

--------------------------------------------------------------------------------
/packages/angular-mcp-server/src/lib/tools/ds/component-contract/list/utils/contract-list-utils.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * Utility functions for contract listing and file operations
 */

import type { ContractFileInfo } from '../models/types.js';

/**
 * Extracts component name from contract filename
 * Handles pattern: componentName-timestamp.contract.json
 */
export function extractComponentNameFromFile(fileName: string): string {
  const baseName = fileName.replace('.contract.json', '');
  const parts = baseName.split('-');

  if (parts.length > 2) {
    let timestampStartIndex = -1;
    for (let i = 1; i < parts.length; i++) {
      if (/^\d{4}$/.test(parts[i])) {
        timestampStartIndex = i;
        break;
      }
    }

    if (timestampStartIndex > 0) {
      return parts.slice(0, timestampStartIndex).join('-');
    }
  }

  return parts[0] || 'unknown';
}

/**
 * Formats byte size into human-readable format
 */
export function formatBytes(bytes: number): string {
  if (bytes === 0) return '0 B';

  const k = 1024;
  const sizes = ['B', 'KB', 'MB'];
  const i = Math.floor(Math.log(bytes) / Math.log(k));

  return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i];
}

/**
 * Converts timestamp to human-readable "time ago" format
 */
export function getTimeAgo(timestamp: string): string {
  const now = new Date();
  const past = new Date(timestamp);
  const diffMs = now.getTime() - past.getTime();

  const minutes = Math.floor(diffMs / (1000 * 60));
  const hours = Math.floor(diffMs / (1000 * 60 * 60));
  const days = Math.floor(diffMs / (1000 * 60 * 60 * 24));

  if (minutes < 1) return 'just now';
  if (minutes < 60) return `${minutes}m ago`;
  if (hours < 24) return `${hours}h ago`;
  if (days < 7) return `${days}d ago`;

  return past.toLocaleDateString();
}

/**
 * Groups contracts by component name and formats them for display output
 */
export function formatContractsByComponent(
  contracts: ContractFileInfo[],
): string[] {
  const output: string[] = [];

  const contractsByComponent = new Map<string, ContractFileInfo[]>();
  contracts.forEach((contract) => {
    const componentContracts =
      contractsByComponent.get(contract.componentName) || [];
    componentContracts.push(contract);
    contractsByComponent.set(contract.componentName, componentContracts);
  });

  contractsByComponent.forEach((componentContracts, componentName) => {
    output.push(`🎯 ${componentName}:`);

    componentContracts.forEach((contract) => {
      const timeAgo = getTimeAgo(contract.timestamp);
      output.push(`   📄 ${contract.fileName}`);
      output.push(`      🔗 ${contract.filePath}`);
      output.push(`      ⏱️  ${timeAgo}`);
      output.push(`      🔑 ${contract.hash.substring(0, 12)}...`);
      output.push(`      📊 ${contract.size}`);
    });

    output.push('');
  });

  return output;
}

```

--------------------------------------------------------------------------------
/packages/shared/typescript-ast-utils/ai/FUNCTIONS.md:
--------------------------------------------------------------------------------

```markdown
# Public API — Quick Reference

| Symbol                 | Kind     | Summary                                                         |
| ---------------------- | -------- | --------------------------------------------------------------- |
| `QUOTE_REGEX`          | constant | Regular expression for matching quotes at start/end of strings  |
| `getDecorators`        | function | Extract decorators from a TypeScript AST node safely            |
| `hasDecorators`        | function | Type guard to check if a node has decorators property           |
| `isComponentDecorator` | function | Check if a decorator is specifically a `@Component` decorator   |
| `isDecorator`          | function | Check if a decorator matches a specific name (or any decorator) |
| `removeQuotes`         | function | Remove surrounding quotes from a string literal AST node        |

## Function Signatures

### `getDecorators(node: ts.Node): readonly ts.Decorator[]`

Safely extracts decorators from a TypeScript AST node, handling different TypeScript compiler API versions.

**Parameters:**

- `node` - The TypeScript AST node to extract decorators from

**Returns:** Array of decorators (empty array if none found)

### `hasDecorators(node: ts.Node): node is ts.Node & { decorators: readonly ts.Decorator[] }`

Type guard function that checks if a node has a decorators property.

**Parameters:**

- `node` - The TypeScript AST node to check

**Returns:** Type predicate indicating if the node has decorators

### `isComponentDecorator(decorator: ts.Decorator): boolean`

Convenience function to check if a decorator is specifically a `@Component` decorator.

**Parameters:**

- `decorator` - The decorator to check

**Returns:** `true` if the decorator is `@Component`, `false` otherwise

### `isDecorator(decorator: ts.Decorator, decoratorName?: string): boolean`

Generic function to check if a decorator matches a specific name or is any valid decorator.

**Parameters:**

- `decorator` - The decorator to check
- `decoratorName` - Optional name to match against (if omitted, checks if it's any valid decorator)

**Returns:** `true` if the decorator matches the criteria, `false` otherwise

### `removeQuotes(node: ts.Node, sourceFile: ts.SourceFile): string`

Removes surrounding quotes from a string literal AST node's text content.

**Parameters:**

- `node` - The TypeScript AST node containing the quoted string
- `sourceFile` - The source file context for getting node text

**Returns:** The string content without surrounding quotes

## Constants

### `QUOTE_REGEX: RegExp`

Regular expression pattern `/^['"`]+|['"`]+$/g` used to match and remove quotes from the beginning and end of strings. Supports single quotes (`'`), double quotes (`"` ), and backticks (``  ` ``).

```

--------------------------------------------------------------------------------
/packages/angular-mcp-server/src/lib/tools/ds/shared/models/schema-helpers.ts:
--------------------------------------------------------------------------------

```typescript
import { ToolSchemaOptions } from '@push-based/models';

/**
 * Common schema property definitions used across DS tools
 * Note: cwd and workspaceRoot are injected by MCP server configuration, not user inputs
 */
export const COMMON_SCHEMA_PROPERTIES = {
  directory: {
    type: 'string' as const,
    description:
      'The relative path to the directory (starting with "./path/to/dir") to scan. Respect the OS specifics.',
  },

  componentName: {
    type: 'string' as const,
    description: 'The class name of the component (e.g., DsButton)',
  },

  groupBy: {
    type: 'string' as const,
    enum: ['file', 'folder'] as const,
    description: 'How to group the results',
    default: 'file' as const,
  },
} as const;

/**
 * Creates a component input schema with a custom description
 */
export const createComponentInputSchema = (
  description: string,
): ToolSchemaOptions['inputSchema'] => ({
  type: 'object',
  properties: {
    componentName: {
      ...COMMON_SCHEMA_PROPERTIES.componentName,
      description,
    },
  },
  required: ['componentName'],
});

/**
 * Creates a directory + component schema for tools that analyze both
 */
export const createDirectoryComponentSchema = (
  componentDescription: string,
  additionalProperties?: Record<string, any>,
): ToolSchemaOptions['inputSchema'] => ({
  type: 'object',
  properties: {
    directory: COMMON_SCHEMA_PROPERTIES.directory,
    componentName: {
      ...COMMON_SCHEMA_PROPERTIES.componentName,
      description: componentDescription,
    },
    ...additionalProperties,
  },
  required: ['directory', 'componentName'],
});

/**
 * Creates a project analysis schema with common project properties
 * Note: cwd and workspaceRoot are handled by MCP server configuration, not user inputs
 */
export const createProjectAnalysisSchema = (
  additionalProperties?: Record<string, any>,
): ToolSchemaOptions['inputSchema'] => ({
  type: 'object',
  properties: {
    directory: COMMON_SCHEMA_PROPERTIES.directory,
    ...additionalProperties,
  },
  required: ['directory'],
});

/**
 * Creates a violation reporting schema with grouping options
 */
export const createViolationReportingSchema = (
  additionalProperties?: Record<string, any>,
): ToolSchemaOptions['inputSchema'] => ({
  type: 'object',
  properties: {
    directory: COMMON_SCHEMA_PROPERTIES.directory,
    componentName: COMMON_SCHEMA_PROPERTIES.componentName,
    groupBy: COMMON_SCHEMA_PROPERTIES.groupBy,
    ...additionalProperties,
  },
  required: ['directory', 'componentName'],
});

/**
 * Common annotation patterns for DS tools
 */
export const COMMON_ANNOTATIONS = {
  readOnly: {
    readOnlyHint: true,
    openWorldHint: true,
    idempotentHint: true,
  },
  project: {
    readOnlyHint: true,
    openWorldHint: true,
    idempotentHint: true,
  },
} as const;

```

--------------------------------------------------------------------------------
/packages/angular-mcp-server/src/lib/tools/ds/component-usage-graph/models/config.ts:
--------------------------------------------------------------------------------

```typescript
import { FileExtension, FileType } from './types.js';
import {
  IMPORT_REGEXES,
  REGEX_CACHE_UTILS,
} from '../../shared/utils/regex-helpers.js';

const STYLES_EXTENSIONS = ['.css', '.scss', '.sass', '.less'] as const;
const SCRIPT_EXTENSIONS = ['.ts', '.tsx', '.js', '.jsx'] as const;
const TEMPLATE_EXTENSIONS = ['.html'] as const;
const FILE_EXTENSIONS = [
  ...STYLES_EXTENSIONS,
  ...SCRIPT_EXTENSIONS,
  ...TEMPLATE_EXTENSIONS,
] as const;
const RESOLVE_EXTENSIONS = ['.ts', '.tsx', '.js', '.jsx'] as const;
const INDEX_FILES = [
  '/index.ts',
  '/index.tsx',
  '/index.js',
  '/index.jsx',
] as const;
const EXCLUDE_PATTERNS = [
  'node_modules',
  'dist',
  'build',
  '.git',
  '.vscode',
  '.idea',
] as const;

const FILE_TYPE_MAP = {
  '.ts': 'typescript',
  '.tsx': 'typescript-react',
  '.js': 'javascript',
  '.jsx': 'javascript-react',
  '.css': 'css',
  '.scss': 'scss',
  '.sass': 'sass',
  '.less': 'less',
  '.html': 'template',
} as const satisfies Record<FileExtension, FileType>;

export const DEPENDENCY_ANALYSIS_CONFIG = {
  stylesExtensions: STYLES_EXTENSIONS,
  scriptExtensions: SCRIPT_EXTENSIONS,
  fileExtensions: FILE_EXTENSIONS,
  resolveExtensions: RESOLVE_EXTENSIONS,
  indexFiles: INDEX_FILES,
  excludePatterns: EXCLUDE_PATTERNS,
  fileTypeMap: FILE_TYPE_MAP,
} as const;

// Use shared regex patterns instead of duplicating them
export const REGEX_PATTERNS = {
  ES6_IMPORT: IMPORT_REGEXES.ES6_IMPORT,
  COMMONJS_REQUIRE: IMPORT_REGEXES.COMMONJS_REQUIRE,
  DYNAMIC_IMPORT: IMPORT_REGEXES.DYNAMIC_IMPORT,
  CSS_IMPORT: IMPORT_REGEXES.CSS_IMPORT,
  CSS_URL: IMPORT_REGEXES.CSS_URL,
  ANGULAR_COMPONENT_DECORATOR: IMPORT_REGEXES.ANGULAR_COMPONENT_DECORATOR,
  GLOB_WILDCARD_REPLACEMENT: /\*/g,
} as const;

// Use shared regex cache utilities instead of duplicating cache management
export const componentImportRegex = (componentName: string): RegExp =>
  REGEX_CACHE_UTILS.getOrCreate(`component-import-${componentName}`, () =>
    IMPORT_REGEXES.createComponentImportRegex(componentName),
  );

export const getComponentImportRegex = (componentName: string): RegExp =>
  componentImportRegex(componentName);

export const getCombinedComponentImportRegex = (
  componentNames: string[],
): RegExp => {
  const cacheKey = componentNames.sort().join('|');
  return REGEX_CACHE_UTILS.getOrCreate(
    `combined-component-import-${cacheKey}`,
    () => IMPORT_REGEXES.createCombinedComponentImportRegex(componentNames),
  );
};

export const clearComponentImportRegexCache = (): void => {
  REGEX_CACHE_UTILS.clear();
};

export const getComponentImportRegexCacheStats = () => {
  const stats = REGEX_CACHE_UTILS.getStats();
  return {
    singleComponentCacheSize: stats.size, // Combined cache now
    combinedComponentCacheSize: 0, // No longer separate
    totalCacheSize: stats.size,
  };
};

```

--------------------------------------------------------------------------------
/packages/minimal-repo/packages/application/src/app/components/refactoring-tests/group-4/multi-violation-test.component.html:
--------------------------------------------------------------------------------

```html
<!-- Multi-violation test component using deprecated CSS classes from 4 DS components -->

<div class="test-container">
  <h2>Multi-Violation Test Component</h2>
  
  <!-- ❌ BAD: DsButton violations - using deprecated 'btn', 'btn-primary', 'legacy-button' -->
  <div class="button-section">
    <h3>Button Violations (DsButton)</h3>
    <button class="btn btn-primary" (click)="handleButtonClick()">
      Primary Legacy Button
    </button>
    <button class="btn">
      Basic Legacy Button  
    </button>
    <button class="legacy-button">
      Legacy Button Style
    </button>
  </div>

  <!-- ❌ BAD: DsBadge violations - using deprecated 'offer-badge' -->
  <div class="badge-section">
    <h3>Badge Violations (DsBadge)</h3>
    <span class="offer-badge">50% OFF</span>
    <div class="product-item">
      <span>Special Product</span>
      <span class="offer-badge">NEW</span>
    </div>
  </div>

  <!-- ❌ BAD: DsTabsModule violations - using deprecated 'tab-nav', 'nav-tabs', 'tab-nav-item' -->
  <div class="tabs-section">
    <h3>Tabs Violations (DsTabsModule)</h3>
    <ul class="nav-tabs tab-nav">
      <li class="tab-nav-item" 
          [class.active]="activeTab === 0"
          (click)="switchTab(0)">
        Tab 1
      </li>
      <li class="tab-nav-item" 
          [class.active]="activeTab === 1"
          (click)="switchTab(1)">
        Tab 2
      </li>
      <li class="tab-nav-item" 
          [class.active]="activeTab === 2"
          (click)="switchTab(2)">
        Tab 3
      </li>
    </ul>
    <div class="tab-content">
      <div *ngIf="activeTab === 0">Content for Tab 1</div>
      <div *ngIf="activeTab === 1">Content for Tab 2</div>
      <div *ngIf="activeTab === 2">Content for Tab 3</div>
    </div>
  </div>

  <!-- ❌ BAD: DsCard violations - using deprecated 'card' -->
  <div class="card-section">
    <h3>Card Violations (DsCard)</h3>
    <div class="card" *ngIf="showCard">
      <div class="card-header">
        <h4>Legacy Card Header</h4>
        <button class="btn" (click)="toggleCard()">×</button>
      </div>
      <div class="card-body">
        <p>This is a legacy card implementation using deprecated CSS classes.</p>
        <span class="offer-badge">FEATURED</span>
      </div>
      <div class="card-footer">
        <button class="legacy-button">Action</button>
      </div>
    </div>
  </div>

  <!-- Mixed violations in a single section -->
  <div class="mixed-section">
    <h3>Mixed Violations</h3>
    <div class="card">
      <nav class="tab-nav">
        <span class="tab-nav-item">Settings</span>
        <span class="offer-badge">{{notifications}}</span>
      </nav>
      <div class="card-body">
        <button class="btn btn-primary">Save Settings</button>
        <button class="legacy-button">Cancel</button>
      </div>
    </div>
  </div>
</div>

```

--------------------------------------------------------------------------------
/packages/angular-mcp-server/src/lib/tools/ds/component-contract/builder/spec/inline-styles.collector.spec.ts:
--------------------------------------------------------------------------------

```typescript
/* eslint-disable prefer-const */
import { describe, it, expect, vi, beforeEach } from 'vitest';

// -----------------------------------------------------------------------------
// Mocks
// -----------------------------------------------------------------------------

// @push-based/styles-ast-utils
let parseStylesheetMock: any;
let visitEachChildMock: any;
vi.mock('@push-based/styles-ast-utils', () => ({
  get parseStylesheet() {
    return parseStylesheetMock;
  },
  get visitEachChild() {
    return visitEachChildMock;
  },
}));
parseStylesheetMock = vi.fn();
visitEachChildMock = vi.fn();

// css-match
let selectorMatchesMock: any;
vi.mock('../utils/css-match.js', () => ({
  get selectorMatches() {
    return selectorMatchesMock;
  },
}));
selectorMatchesMock = vi.fn();

// SUT
import { collectInlineStyles } from '../utils/inline-styles.collector.js';

// -----------------------------------------------------------------------------
// Helpers
// -----------------------------------------------------------------------------
function createRule(selector: string, decls: Record<string, string>) {
  return {
    selector,
    walkDecls(cb: (decl: { prop: string; value: string }) => void) {
      Object.entries(decls).forEach(([prop, value]) => cb({ prop, value }));
    },
  } as any;
}

function resetMocks() {
  parseStylesheetMock.mockReset();
  visitEachChildMock.mockReset();
  selectorMatchesMock.mockReset();
}

// -----------------------------------------------------------------------------
// Tests
// -----------------------------------------------------------------------------

describe('collectInlineStyles', () => {
  beforeEach(() => {
    resetMocks();
    // Provide a simple PostCSS root mock
    parseStylesheetMock.mockReturnValue({ root: { type: 'root' } });
  });

  it('returns style declarations from inline styles', async () => {
    // Fake ParsedComponent with inline styles array
    const parsedComponent = {
      fileName: '/cmp.ts',
      styles: [
        {
          parse: async () => ({
            toString: () => '.btn{color:red}',
          }),
        },
      ],
    } as any;

    // DOM snapshot with one matching element
    const dom = {
      button: {} as any,
    };

    visitEachChildMock.mockImplementation((_root: any, visitor: any) => {
      visitor.visitRule(createRule('.btn', { color: 'red' }));
    });

    selectorMatchesMock.mockImplementation(
      (cssSel: string, domKey: string) =>
        cssSel === '.btn' && domKey === 'button',
    );

    const styles = await collectInlineStyles(parsedComponent, dom as any);

    expect(styles.sourceFile).toBe('/cmp.ts');
    expect(styles.rules['.btn']).toBeDefined();
    expect(styles.rules['.btn'].properties).toEqual({ color: 'red' });
    expect(styles.rules['.btn'].appliesTo).toEqual(['button']);
  });
});

```

--------------------------------------------------------------------------------
/packages/angular-mcp-server/src/lib/tools/ds/component-contract/diff/diff-component-contract.tool.ts:
--------------------------------------------------------------------------------

```typescript
import {
  createHandler,
  BaseHandlerOptions,
} from '../../shared/utils/handler-helpers.js';
import {
  resolveCrossPlatformPath,
  normalizePathsInObject,
} from '../../shared/utils/cross-platform-path.js';
import { diffComponentContractSchema } from './models/schema.js';
import type { DomPathDictionary } from '../shared/models/types.js';
import { loadContract } from '../shared/utils/contract-file-ops.js';
import {
  consolidateAndPruneRemoveOperationsWithDeduplication,
  groupChangesByDomainAndType,
  generateDiffSummary,
} from './utils/diff-utils.js';
import { writeFile, mkdir } from 'node:fs/promises';
import diff from 'microdiff';

interface DiffComponentContractOptions extends BaseHandlerOptions {
  saveLocation: string;
  contractBeforePath: string;
  contractAfterPath: string;
  dsComponentName?: string;
}

export const diffComponentContractHandler = createHandler<
  DiffComponentContractOptions,
  {
    fileUrl: string;
    domPathStats?: DomPathDictionary['stats'];
  }
>(
  diffComponentContractSchema.name,
  async (params, { cwd, workspaceRoot }) => {
    const {
      saveLocation,
      contractBeforePath,
      contractAfterPath,
      dsComponentName = '',
    } = params;

    const effectiveBeforePath = resolveCrossPlatformPath(
      cwd,
      contractBeforePath,
    );
    const effectiveAfterPath = resolveCrossPlatformPath(cwd, contractAfterPath);

    const contractBefore = await loadContract(effectiveBeforePath);
    const contractAfter = await loadContract(effectiveAfterPath);

    const rawDiffResult = diff(contractBefore, contractAfter);

    const { processedResult, domPathDict } =
      consolidateAndPruneRemoveOperationsWithDeduplication(rawDiffResult);

    const groupedChanges = groupChangesByDomainAndType(processedResult);

    const diffData = {
      before: effectiveBeforePath,
      after: effectiveAfterPath,
      dsComponentName,
      timestamp: new Date().toISOString(),
      domPathDictionary: domPathDict.paths,
      changes: groupedChanges,
      summary: generateDiffSummary(processedResult, groupedChanges),
    };

    const normalizedDiffData = normalizePathsInObject(diffData, workspaceRoot);

    const effectiveSaveLocation = resolveCrossPlatformPath(cwd, saveLocation);

    const { dirname } = await import('node:path');
    await mkdir(dirname(effectiveSaveLocation), { recursive: true });

    const diffFilePath = effectiveSaveLocation;

    const formattedJson = JSON.stringify(normalizedDiffData, null, 2);
    await writeFile(diffFilePath, formattedJson, 'utf-8');

    return {
      fileUrl: `file://${diffFilePath}`,
      domPathStats: domPathDict.stats,
    };
  },
  (result) => {
    return [result.fileUrl];
  },
);

export const diffComponentContractTools = [
  {
    schema: diffComponentContractSchema,
    handler: diffComponentContractHandler,
  },
];

```

--------------------------------------------------------------------------------
/packages/shared/ds-component-coverage/package.json:
--------------------------------------------------------------------------------

```json
{
  "name": "@push-based/ds-component-coverage",
  "version": "0.0.1",
  "private": true,
  "type": "module",
  "main": "./dist/index.js",
  "module": "./dist/index.js",
  "types": "./dist/index.d.ts",
  "exports": {
    "./package.json": "./package.json",
    ".": {
      "development": "./src/index.ts",
      "types": "./dist/index.d.ts",
      "import": "./dist/index.js",
      "default": "./dist/index.js"
    }
  },
  "nx": {
    "name": "ds-component-coverage",
    "targets": {
      "ds-component-coverage:demo": {
        "command": "npx @code-pushup/cli collect --config=packages/shared/ds-component-coverage/mocks/fixtures/e2e/demo/code-pushup.config.ts",
        "options": {
          "onlyPlugins": "ds-component-coverage",
          "progress": false
        }
      },
      "ds-component-coverage:asset-location": {
        "command": "npx @code-pushup/cli collect --config=packages/ds-component-coverage/mocks/fixtures/e2e/asset-location/code-pushup.config.ts",
        "options": {
          "onlyPlugins": "ds-component-coverage",
          "progress": false
        }
      },
      "ds-component-coverage:line-number": {
        "command": "npx @code-pushup/cli collect --config=packages/ds-component-coverage/mocks/fixtures/e2e/line-number/code-pushup.config.ts",
        "options": {
          "onlyPlugins": "ds-component-coverage",
          "progress": false
        }
      },
      "ds-component-coverage:style-format": {
        "command": "npx @code-pushup/cli collect --config=packages/ds-component-coverage/mocks/fixtures/e2e/style-format/code-pushup.config.ts",
        "options": {
          "onlyPlugins": "ds-component-coverage",
          "progress": false
        }
      },
      "ds-component-coverage:template-syntax": {
        "command": "npx @code-pushup/cli collect --config=packages/ds-component-coverage/mocks/fixtures/e2e/template-syntax/code-pushup.config.ts",
        "options": {
          "onlyPlugins": "ds-component-coverage",
          "progress": false
        }
      },
      "ds-quality:demo": {
        "command": "npx @code-pushup/cli collect --config=packages/ds-quality/mocks/fixtures/minimal-design-system/code-pushup.config.ts",
        "options": {
          "onlyPlugins": "ds-quality",
          "progress": false
        }
      },
      "ds-quality:variable-usage": {
        "command": "npx @code-pushup/cli collect --config=packages/ds-quality/mocks/fixtures/variable-usage/code-pushup.config.ts",
        "options": {
          "onlyPlugins": "ds-quality",
          "progress": false
        }
      },
      "ds-quality:mixin-usage": {
        "command": "npx @code-pushup/cli collect --config=packages/ds-quality/mocks/fixtures/mixin-usage/code-pushup.config.ts",
        "options": {
          "onlyPlugins": "ds-quality",
          "progress": false
        }
      }
    }
  }
}

```

--------------------------------------------------------------------------------
/packages/angular-mcp-server/src/lib/tools/ds/project/report-deprecated-css.tool.ts:
--------------------------------------------------------------------------------

```typescript
import { ToolSchemaOptions } from '@push-based/models';
import * as path from 'node:path';
import {
  createHandler,
  BaseHandlerOptions,
  RESULT_FORMATTERS,
} from '../shared/utils/handler-helpers.js';
import {
  createDirectoryComponentSchema,
  COMMON_ANNOTATIONS,
} from '../shared/models/schema-helpers.js';
import { getDeprecatedCssClasses } from '../component/utils/deprecated-css-helpers.js';
import {
  findStyleFiles,
  analyzeStyleFile,
} from './utils/styles-report-helpers.js';
import { resolveCrossPlatformPath } from '../shared/utils/cross-platform-path.js';

interface ReportDeprecatedCssOptions extends BaseHandlerOptions {
  directory: string;
  componentName: string;
  deprecatedCssClassesPath?: string;
}

export const reportDeprecatedCssSchema: ToolSchemaOptions = {
  name: 'report-deprecated-css',
  description: `Report deprecated CSS classes found in styling files in a directory.`,
  inputSchema: createDirectoryComponentSchema(
    'The class name of the component to get deprecated classes for (e.g., DsButton)',
  ),
  annotations: {
    title: 'Report Deprecated CSS',
    ...COMMON_ANNOTATIONS.readOnly,
  },
};

export const reportDeprecatedCssHandler = createHandler<
  ReportDeprecatedCssOptions,
  string[]
>(
  reportDeprecatedCssSchema.name,
  async (params, { cwd, deprecatedCssClassesPath }) => {
    const { directory, componentName } = params;

    if (!deprecatedCssClassesPath) {
      throw new Error(
        'Missing ds.deprecatedCssClassesPath. Provide --ds.deprecatedCssClassesPath in mcp.json file.',
      );
    }

    const deprecated = await getDeprecatedCssClasses(
      componentName,
      deprecatedCssClassesPath,
      cwd,
    );

    if (!deprecated.length) {
      return [`No deprecated CSS classes defined for ${componentName}`];
    }

    const styleFiles = await findStyleFiles(
      resolveCrossPlatformPath(cwd, directory),
    );

    if (!styleFiles.length) {
      return [`No styling files found in ${directory}`];
    }

    const results = await Promise.all(
      styleFiles.map((f) => analyzeStyleFile(f, deprecated)),
    );

    const violations: string[] = [];

    for (const { filePath, foundClasses } of results) {
      if (!foundClasses.length) continue;

      const relativePath = path.relative(cwd, filePath);

      for (const { className, lineNumber } of foundClasses) {
        const lineInfo = lineNumber ? ` (line ${lineNumber})` : '';
        violations.push(
          `${relativePath}${lineInfo}: The selector's class \`${className}\` is deprecated.`,
        );
      }
    }

    return violations.length ? violations : ['No deprecated CSS classes found'];
  },
  (result) => RESULT_FORMATTERS.list(result, 'Design System CSS Violations:'),
);

export const reportDeprecatedCssTools = [
  {
    schema: reportDeprecatedCssSchema,
    handler: reportDeprecatedCssHandler,
  },
];

```

--------------------------------------------------------------------------------
/packages/angular-mcp-server/src/lib/tools/ds/component/utils/paths-helpers.ts:
--------------------------------------------------------------------------------

```typescript
import * as fs from 'fs';
import * as path from 'path';
import {
  validateComponentName,
  componentNameToKebabCase,
} from '../../shared/utils/component-validation.js';
import { resolveCrossPlatformPath } from '../../shared/utils/cross-platform-path.js';

export interface ComponentPaths {
  componentName: string;
  folderSlug: string;
  srcPath: string;
  packageJsonPath: string;
}

export interface ComponentPathsInfo {
  srcPath: string;
  importPath: string;
  relativeSrcPath: string;
}

export function getComponentPaths(
  uiRoot: string,
  componentName: string,
): ComponentPaths {
  const folderSlug = componentNameToKebabCase(componentName);
  const componentFolder = path.join(uiRoot, folderSlug);
  const srcPath = path.join(componentFolder, 'src');
  const packageJsonPath = path.join(componentFolder, 'package.json');

  return {
    componentName,
    folderSlug,
    srcPath,
    packageJsonPath,
  };
}

export function getImportPathFromPackageJson(
  packageJsonPath: string,
): string | null {
  try {
    if (!fs.existsSync(packageJsonPath)) {
      return null;
    }

    const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
    return packageJson.name || null;
  } catch (ctx) {
    throw new Error(
      `Error reading import path from package.json: ${(ctx as Error).message}`,
    );
  }
}

/**
 * Reusable helper function to get component paths information
 * @param componentName - The class name of the component (e.g., DsBadge)
 * @param uiRoot - The UI root directory path
 * @param cwd - Current working directory (optional, defaults to process.cwd())
 * @returns Object containing source path and import path information
 */
export function getComponentPathsInfo(
  componentName: string,
  uiRoot: string,
  cwd: string = process.cwd(),
): ComponentPathsInfo {
  try {
    validateComponentName(componentName);

    if (!uiRoot || typeof uiRoot !== 'string') {
      throw new Error('uiRoot must be provided and be a string path.');
    }

    const componentsBasePath = resolveCrossPlatformPath(cwd, uiRoot);
    const componentPaths = getComponentPaths(componentsBasePath, componentName);

    const relativeComponentPaths = getComponentPaths(uiRoot, componentName);

    if (!fs.existsSync(componentPaths.srcPath)) {
      throw new Error(
        `Component source directory not found: ${relativeComponentPaths.srcPath}`,
      );
    }

    const importPath = getImportPathFromPackageJson(
      componentPaths.packageJsonPath,
    );

    if (!importPath) {
      throw new Error(
        `Could not read import path from package.json for component: ${componentName}`,
      );
    }

    return {
      srcPath: componentPaths.srcPath,
      importPath,
      relativeSrcPath: relativeComponentPaths.srcPath,
    };
  } catch (ctx) {
    throw new Error(
      `Error retrieving component information: ${(ctx as Error).message}`,
    );
  }
}

```

--------------------------------------------------------------------------------
/packages/minimal-repo/packages/design-system/storybook-host-app/src/components/modal/modal-tabs/overview.mdx:
--------------------------------------------------------------------------------

```markdown
You are tasked with creating a comprehensive refactoring plan for migrating legacy markup to a single design-system component. This plan should cover templates, class TypeScript files, styles, NgModules, and specs surfaced by the usage graph. Follow these instructions carefully to generate the plan:

1. Review the following input variables:
<component_name>
{{COMPONENT_NAME}}
</component_name>

<subfolder>
{{SUBFOLDER}}
</subfolder>

<violations>
{{VIOLATIONS}}
</violations>

<scan_result>
{{SCAN_RESULT}}
</scan_result>

<file_scan>
{{FILE_SCAN}}
</file_scan>

2. Acquire reference material:
   a. Call the function get-component-docs with the component name as the argument.
   b. If there's an error or no docs are found, output a commentary message and stop.
   c. Parse the docs to create a dsExemplar object containing markup, required parts, optional parts, and API information.

3. Map dependencies and impact:
   a. Call the function build-component-usage-graph with the subfolder as the argument.
   b. If there's an error or any violation file is missing from the graph, output a commentary message and stop.
   c. Derive working sets for host templates, classes, styles, specs, and modules.

4. Generate baseline contracts:
   a. For each host template, call the build_component_contract function.
   b. If there's a contract build error, output a commentary message and stop.

5. Analyze refactorability:
   a. Compare each violation instance with the dsExemplar markup.
   b. Classify as "non-viable", "requires-restructure", or "simple-swap".
   c. Record template edits, CSS clean-up, and ancillary edits.
   d. Calculate complexity scores.
   e. Aggregate results into filePlans.

6. Synthesize and output the plan:
   a. Sort filePlans by complexity score, violation count, and file path.
   b. Generate the plan in the following format:

<plan>
[For each file in filePlans, include:
- File path
- File type (template|class|style|spec|module)
- Refactor class (non-viable|requires-restructure|simple-swap)
- Actions to take (bullet points for template edits, TS updates, style removal, NgModule changes, spec tweaks)
- Complexity score]
</plan>

7. After the plan, ask the following question:
🛠️ Approve this plan or specify adjustments?

Important reminders:
- Maintain NgModule-based structure (no stand-alone conversion).
- Generate contracts only for host components (template + class).
- Output must be minimal: a single <plan> block followed by one question.
- All other messages or errors should be enclosed in <commentary> tags.
- Ensure every host module needing a new import appears in filePlans.
- Ensure every host spec appears in filePlans (even if action="none").
- Verify that dsExemplar was referenced at least once.

Your final output should consist of only the <plan> block and the follow-up question. Any additional comments or error messages should be enclosed in <commentary> tags.
```

--------------------------------------------------------------------------------
/packages/angular-mcp-server/src/lib/tools/ds/component-contract/builder/build-component-contract.tool.ts:
--------------------------------------------------------------------------------

```typescript
import {
  createHandler,
  BaseHandlerOptions,
} from '../../shared/utils/handler-helpers.js';
import { buildComponentContractSchema } from './models/schema.js';
import { buildComponentContract } from './utils/build-contract.js';
import { generateContractSummary } from '../shared/utils/contract-file-ops.js';
import { ContractResult } from './models/types.js';
import { resolveCrossPlatformPath } from '../../shared/utils/cross-platform-path.js';
import { createHash } from 'node:crypto';

interface BuildComponentContractOptions extends BaseHandlerOptions {
  saveLocation: string;
  templateFile?: string;
  styleFile?: string;
  typescriptFile: string;
  dsComponentName?: string;
}

export const buildComponentContractHandler = createHandler<
  BuildComponentContractOptions,
  ContractResult
>(
  buildComponentContractSchema.name,
  async (params, { cwd, workspaceRoot: _workspaceRoot }) => {
    const {
      saveLocation,
      templateFile,
      styleFile,
      typescriptFile,
      dsComponentName = '',
    } = params;

    const effectiveTypescriptPath = resolveCrossPlatformPath(
      cwd,
      typescriptFile,
    );

    // If templateFile or styleFile are not provided, use the TypeScript file path
    // This indicates inline template/styles
    const effectiveTemplatePath = templateFile
      ? resolveCrossPlatformPath(cwd, templateFile)
      : effectiveTypescriptPath;
    const effectiveScssPath = styleFile
      ? resolveCrossPlatformPath(cwd, styleFile)
      : effectiveTypescriptPath;

    const contract = await buildComponentContract(
      effectiveTemplatePath,
      effectiveScssPath,
      cwd,
      effectiveTypescriptPath,
    );

    const contractString = JSON.stringify(contract, null, 2);
    const hash = createHash('sha256').update(contractString).digest('hex');

    const effectiveSaveLocation = resolveCrossPlatformPath(cwd, saveLocation);

    const { mkdir, writeFile } = await import('node:fs/promises');
    const { dirname } = await import('node:path');
    await mkdir(dirname(effectiveSaveLocation), { recursive: true });

    const contractData = {
      contract,
      hash: `sha256-${hash}`,
      metadata: {
        templatePath: effectiveTemplatePath,
        scssPath: effectiveScssPath,
        typescriptPath: effectiveTypescriptPath,
        timestamp: new Date().toISOString(),
        dsComponentName,
      },
    };

    await writeFile(
      effectiveSaveLocation,
      JSON.stringify(contractData, null, 2),
      'utf-8',
    );

    const contractFilePath = effectiveSaveLocation;

    return {
      contract,
      hash: `sha256-${hash}`,
      contractFilePath,
    };
  },
  (result) => {
    const summary = generateContractSummary(result.contract);
    return [
      `✅ Contract Hash: ${result.hash}`,
      `📁 Saved to: ${result.contractFilePath}`,
      ...summary,
    ];
  },
);

export const buildComponentContractTools = [
  {
    schema: buildComponentContractSchema,
    handler: buildComponentContractHandler,
  },
];

```

--------------------------------------------------------------------------------
/packages/angular-mcp/src/main.ts:
--------------------------------------------------------------------------------

```typescript
#!/usr/bin/env node
import express from 'express';
import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import yargs from 'yargs';
import { hideBin } from 'yargs/helpers';
import { AngularMcpServerWrapper } from '@push-based/angular-mcp-server';

interface ArgvType {
  sse: boolean;
  port?: number;
  _: (string | number)[];
  $0: string;

  [x: string]: unknown;
}

const argv = yargs(hideBin(process.argv))
  .command('$0', 'Start the angular-mcp server')
  .option('workspaceRoot', {
    describe: 'The root directory of the workspace as absolute path',
    type: 'string',
    required: true,
  })
  .option('ds.storybookDocsRoot', {
    describe:
      'The root directory of the storybook docs relative from workspace root',
    type: 'string',
  })
  .option('ds.deprecatedCssClassesPath', {
    describe:
      'The path to the deprecated classes file relative from workspace root',
    type: 'string',
  })
  .option('ds.uiRoot', {
    describe:
      'The root directory of the actual Angular components relative from workspace root',
    type: 'string',
  })
  .option('sse', {
    describe: 'Configure the server to use SSE (Server-Sent Events)',
    type: 'boolean',
    default: false,
  })
  .option('port', {
    alias: 'p',
    describe: 'Port to use for the SSE server (default: 9921)',
    type: 'number',
  })
  .check((argv) => {
    if (argv.port !== undefined && !argv.sse) {
      throw new Error(
        'The --port option can only be used when --sse is enabled',
      );
    }
    return true;
  })
  .help()
  .parseSync() as ArgvType;

const { workspaceRoot, ds } = argv as unknown as {
  workspaceRoot: string;
  ds: {
    storybookDocsRoot?: string;
    deprecatedCssClassesPath?: string;
    uiRoot: string;
  };
};
const { storybookDocsRoot, deprecatedCssClassesPath, uiRoot } = ds;

async function startServer() {
  const server = await AngularMcpServerWrapper.create({
    workspaceRoot: workspaceRoot as string,
    ds: {
      storybookDocsRoot,
      deprecatedCssClassesPath,
      uiRoot,
    },
  });

  if (argv.sse) {
    const port = argv.port ?? 9921;

    const app = express();
    let transport: SSEServerTransport;
    app.get('/sse', async (_, res) => {
      transport = new SSEServerTransport('/messages', res);
      await server.getMcpServer().connect(transport);
    });

    app.post('/messages', async (req, res) => {
      if (!transport) {
        res.status(400).send('No transport found');
        return;
      }
      await transport.handlePostMessage(req, res);
    });

    const server_instance = app.listen(port);

    process.on('exit', () => {
      server_instance.close();
    });
  } else {
    const transport = new StdioServerTransport();
    server.getMcpServer().connect(transport);
  }
}

// eslint-disable-next-line unicorn/prefer-top-level-await
startServer().catch((ctx) => {
  console.error('Failed to start server:', ctx);
  process.exit(1);
});

```

--------------------------------------------------------------------------------
/testing/utils/src/lib/os-agnostic-paths.ts:
--------------------------------------------------------------------------------

```typescript
const AGNOSTIC_PATH_SEP_REGEX = /[/\\]/g;
const OS_AGNOSTIC_PATH_SEP = '/';
const OS_AGNOSTIC_CWD = `<CWD>`;

/**
 * Converts a given file path to an OS-agnostic path by replacing the current working directory with '<CWD>'
 * and normalizing path separators to '/'.
 *
 * @param filePath - The file path to be converted.
 * @param separator - The path separator to use for normalization. Defaults to the OS-specific separator.
 * @returns The OS-agnostic path.
 *
 * @example
 *
 * At CWD on Ubuntu (Linux)
 * Input: /home/projects/my-folder/my-file.ts
 * Output: <CWD>/my-folder/my-file.ts
 *
 * At CWD on Windows
 * Input: D:\projects\my-folder\my-file.ts
 * Output: <CWD>/my-folder/my-file.ts
 *
 * At CWD on macOS
 * Input: /Users/projects/my-folder/my-file.ts
 * Output: <CWD>/my-folder/my-file.ts
 *
 * Out of CWD on all OS
 * Input: /Users/projects/../my-folder/my-file.ts
 * Output: ../my-folder/my-file.ts
 *
 * Absolute paths (all OS)
 * Input: \\my-folder\\my-file.ts
 * Output: /my-folder/my-file.ts
 *
 * Relative paths (all OS)
 * Input: ..\\my-folder\\my-file.ts
 * Output: ../my-folder/my-file.ts
 *
 */

export function osAgnosticPath(filePath: undefined): undefined;
export function osAgnosticPath(filePath: string): string;
export function osAgnosticPath(filePath?: string): string | undefined {
  if (filePath == null) {
    return filePath;
  }
  // prepare the path for comparison
  // normalize path separators od cwd: "Users\\repo" => "Users/repo"
  const osAgnosticCwd = process
    .cwd()
    .split(AGNOSTIC_PATH_SEP_REGEX)
    .join(OS_AGNOSTIC_PATH_SEP);
  // normalize path separators  => "..\\folder\\repo.ts" => => "../folder/repo.ts"
  const osAgnosticFilePath = filePath
    .split(AGNOSTIC_PATH_SEP_REGEX)
    .join(OS_AGNOSTIC_PATH_SEP);
  // remove the current working directory for easier comparison
  const osAgnosticPathWithoutCwd = osAgnosticFilePath
    .replace(osAgnosticCwd, '')
    // consider already agnostic paths
    .replace(OS_AGNOSTIC_CWD, '');

  // path is outside cwd (Users/repo/../my-folder/my-file.ts)
  if (
    osAgnosticPathWithoutCwd.startsWith(
      `${OS_AGNOSTIC_PATH_SEP}..${OS_AGNOSTIC_PATH_SEP}`,
    )
  ) {
    return osAgnosticPathWithoutCwd.slice(1); // remove the leading '/'
  }

  // path is at cwd (Users/repo/my-folder/my-file.ts)
  if (
    osAgnosticFilePath.startsWith(osAgnosticCwd) ||
    osAgnosticFilePath.startsWith(OS_AGNOSTIC_CWD)
  ) {
    // Add a substitute for the current working directory
    return `${OS_AGNOSTIC_CWD}${osAgnosticPathWithoutCwd}`;
  }

  // Notice: I kept the following conditions for documentation purposes

  // path is absolute (/my-folder/my-file.ts)
  if (osAgnosticPathWithoutCwd.startsWith(OS_AGNOSTIC_PATH_SEP)) {
    return osAgnosticPathWithoutCwd;
  }

  // path is relative (./my-folder/my-file.ts)
  if (osAgnosticPathWithoutCwd.startsWith(`.${OS_AGNOSTIC_PATH_SEP}`)) {
    return osAgnosticPathWithoutCwd;
  }

  // path is segment (my-folder/my-file.ts or my-folder/sub-folder)
  return osAgnosticPathWithoutCwd;
}

```

--------------------------------------------------------------------------------
/packages/minimal-repo/packages/design-system/storybook-host-app/src/components/segmented-control/segmented-control-tabs/api.mdx:
--------------------------------------------------------------------------------

```markdown
## Inputs

### DsSegmentedControl

| Name                | Type                          | Default     | Description                                                   |
| ------------------- | ----------------------------- | ----------- | ------------------------------------------------------------- |
| `activeOption`      | `string`                      | `''`        | Name of the currently selected option.                        |
| `fullWidth`         | `boolean`                     | `false`     | Whether the segmented control takes the full container width. |
| `inverse`           | `boolean`                     | `false`     | Applies inverse color scheme to the component.                |
| `roleType`          | `'radiogroup'` \| `'tablist'` | `'tablist'` | Sets the ARIA role of the segmented control group.            |
| `twoLineTruncation` | `boolean`                     | `false`     | Enables two-line truncation for long labels.                  |

### DsSegmentedOption

| Name    | Type     | Required | Default | Description                     |
| ------- | -------- | -------- | ------- | ------------------------------- |
| `name`  | `string` | Yes      | —       | Unique name for the option.     |
| `title` | `string` | No       | `''`    | Displayed label for the option. |

<sup>**1**</sup> **activeOption note:** **name** needs to be added to segmented
option to have possibility to define selected item

---

## Outputs

### DsSegmentedControl

| Name                 | Type                   | Description                                   |
| -------------------- | ---------------------- | --------------------------------------------- |
| `activeOptionChange` | `EventEmitter<string>` | Emits the new selected option name on change. |

```json
{
  "index": 2,
  "event": {
    "isTrusted": true,
    "altKey": false,
    "altitudeAngle": 1.5707963267948966,
    "azimuthAngle": 0,
    "bubbles": true,
    "button": 0,
    "buttons": 0,
    "cancelBubble": false,
    "cancelable": true,
    "clientX": 433,
    "clientY": 85,
    "composed": true,
    "ctrlKey": false
  }
}
```

### DsSegmentedOption

| Name           | Type                   | Description                        |
| -------------- | ---------------------- | ---------------------------------- |
| `selectOption` | `EventEmitter<string>` | Emits when the option is selected. |

---

## Content Projection

| Selector      | Description                                |
| ------------- | ------------------------------------------ |
| `#dsTemplate` | A `TemplateRef` for custom option content. |

---

## Host Bindings

### DsSegmentedControl

- `class="ds-segmented-control"` — base class
- `class.ds-segment-full-width` — applied when `fullWidth` is true
- `class.ds-segment-inverse` — applied when `inverse` is true
- `class.ds-sc-ready` — applied after view initialization
- `role` — set to `tablist` or `radiogroup` depending on `roleType`

### DsSegmentedOption

- Focus state and tabindex are controlled dynamically.
- Host uses `rxHostPressedListener` for click-based selection.

```

--------------------------------------------------------------------------------
/packages/minimal-repo/packages/application/src/app/components/refactoring-tests/group-4/multi-violation-test.component.scss:
--------------------------------------------------------------------------------

```scss
/* Multi-violation test component styles using deprecated CSS classes */

.test-container {
  padding: 20px;
  max-width: 800px;
  margin: 0 auto;
}

/* ❌ BAD: DsButton deprecated styles - 'btn', 'btn-primary', 'legacy-button' */
.btn {
  display: inline-block;
  padding: 8px 16px;
  margin: 4px;
  border: 1px solid #ccc;
  border-radius: 4px;
  background-color: #f8f9fa;
  color: #333;
  cursor: pointer;
  font-size: 14px;
  text-decoration: none;
  
  &:hover {
    background-color: #e9ecef;
  }
}

.btn-primary {
  background-color: #007bff;
  border-color: #007bff;
  color: white;
  
  &:hover {
    background-color: #0056b3;
  }
}

.legacy-button {
  background: linear-gradient(45deg, #ff6b6b, #feca57);
  border: none;
  padding: 10px 20px;
  color: white;
  border-radius: 8px;
  cursor: pointer;
  font-weight: bold;
  margin: 4px;
  
  &:hover {
    transform: translateY(-2px);
    box-shadow: 0 4px 8px rgba(0,0,0,0.2);
  }
}

/* ❌ BAD: DsBadge deprecated styles - 'offer-badge' */
.offer-badge {
  background-color: #ff4757;
  color: white;
  padding: 4px 8px;
  border-radius: 12px;
  font-size: 12px;
  font-weight: bold;
  text-transform: uppercase;
  margin-left: 8px;
  display: inline-block;
  
  &:before {
    content: "🔥 ";
  }
}

/* ❌ BAD: DsTabsModule deprecated styles - 'tab-nav', 'nav-tabs', 'tab-nav-item' */
.nav-tabs {
  display: flex;
  list-style: none;
  padding: 0;
  margin: 0;
  border-bottom: 2px solid #dee2e6;
}

.tab-nav {
  background-color: #f8f9fa;
  border-radius: 4px 4px 0 0;
}

.tab-nav-item {
  padding: 12px 16px;
  cursor: pointer;
  border: 1px solid transparent;
  border-bottom: none;
  background-color: #f8f9fa;
  color: #495057;
  
  &:hover {
    background-color: #e9ecef;
  }
  
  &.active {
    background-color: white;
    border-color: #dee2e6;
    color: #007bff;
    border-bottom: 2px solid white;
    margin-bottom: -2px;
  }
}

/* ❌ BAD: DsCard deprecated styles - 'card' */
.card {
  border: 1px solid #dee2e6;
  border-radius: 8px;
  background-color: white;
  box-shadow: 0 2px 4px rgba(0,0,0,0.1);
  margin: 16px 0;
  overflow: hidden;
}

.card-header {
  padding: 16px;
  background-color: #f8f9fa;
  border-bottom: 1px solid #dee2e6;
  display: flex;
  justify-content: space-between;
  align-items: center;
  
  h4 {
    margin: 0;
    color: #495057;
  }
}

.card-body {
  padding: 16px;
  
  p {
    margin: 0 0 12px 0;
    color: #6c757d;
  }
}

.card-footer {
  padding: 16px;
  background-color: #f8f9fa;
  border-top: 1px solid #dee2e6;
  text-align: right;
}

/* Section styling */
.button-section,
.badge-section,
.tabs-section,
.card-section,
.mixed-section {
  margin-bottom: 32px;
  
  h3 {
    color: #495057;
    border-bottom: 1px solid #dee2e6;
    padding-bottom: 8px;
    margin-bottom: 16px;
  }
}

.product-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 8px;
  border: 1px solid #dee2e6;
  border-radius: 4px;
  margin: 8px 0;
  background-color: #f8f9fa;
}

.tab-content {
  padding: 20px;
  border: 1px solid #dee2e6;
  border-top: none;
  background-color: white;
  min-height: 100px;
}

```

--------------------------------------------------------------------------------
/packages/angular-mcp-server/src/lib/tools/ds/component-contract/builder/spec/typescript-analyzer.spec.ts:
--------------------------------------------------------------------------------

```typescript
import { describe, it, expect } from 'vitest';

import ts from 'typescript';

import {
  extractPublicMethods,
  extractLifecycleHooks,
  extractImports,
} from '../utils/typescript-analyzer.js';

// -----------------------------------------------------------------------------
// Helpers
// -----------------------------------------------------------------------------

function parseSource(code: string, fileName = 'comp.ts') {
  return ts.createSourceFile(
    fileName,
    code,
    ts.ScriptTarget.Latest,
    true,
    ts.ScriptKind.TS,
  );
}

function getFirstClass(sourceFile: ts.SourceFile): ts.ClassDeclaration {
  const cls = sourceFile.statements.find(ts.isClassDeclaration);
  if (!cls) throw new Error('Class not found');
  return cls;
}

// -----------------------------------------------------------------------------
// Tests
// -----------------------------------------------------------------------------

describe('typescript-analyzer utilities', () => {
  it('extractPublicMethods returns only public non-lifecycle methods', () => {
    const code = `
      class MyComponent {
        foo(a: number): void {}
        private hidden() {}
        protected prot() {}
        ngOnInit() {}
        static util() {}
        async fetchData() {}
      }
    `;

    const sf = parseSource(code);
    const cls = getFirstClass(sf);

    const methods = extractPublicMethods(cls, sf);

    expect(methods).toHaveProperty('foo');
    expect(methods).toHaveProperty('util');
    expect(methods).toHaveProperty('fetchData');
    expect(methods).not.toHaveProperty('hidden');
    expect(methods).not.toHaveProperty('prot');
    expect(methods).not.toHaveProperty('ngOnInit');

    const foo = methods.foo;
    expect(foo.parameters[0]).toEqual(
      expect.objectContaining({ name: 'a', type: 'number' }),
    );
    expect(foo.isStatic).toBe(false);
    expect(foo.isAsync).toBe(false);

    expect(methods.util.isStatic).toBe(true);
    expect(methods.fetchData.isAsync).toBe(true);
  });

  it('extractLifecycleHooks detects implemented and method-based hooks', () => {
    const code = `
      interface OnInit { ngOnInit(): void; }
      class MyComponent implements OnInit {
        ngOnInit() {}
        ngAfterViewInit() {}
        foo() {}
      }
    `;

    const sf = parseSource(code);
    const cls = getFirstClass(sf);

    const hooks = extractLifecycleHooks(cls);
    expect(hooks).toEqual(expect.arrayContaining(['OnInit', 'AfterViewInit']));
  });

  it('extractImports lists imported symbols with their paths', () => {
    const code = `
      import { HttpClient } from '@angular/common/http';
      import * as _ from 'lodash';
      import defaultExport from 'lib';
      import { MatButtonModule as MB } from '@angular/material/button';
    `;

    const sf = parseSource(code);
    const imports = extractImports(sf);

    expect(imports).toEqual(
      expect.arrayContaining([
        { name: 'HttpClient', path: '@angular/common/http' },
        { name: '_', path: 'lodash' },
        { name: 'defaultExport', path: 'lib' },
        { name: 'MB', path: '@angular/material/button' },
      ]),
    );
  });
});

```

--------------------------------------------------------------------------------
/packages/shared/ds-component-coverage/src/lib/runner/audits/ds-coverage/class-definition.visitor.ts:
--------------------------------------------------------------------------------

```typescript
import { Rule } from 'postcss';
import { Issue } from '@code-pushup/models';
import { DiagnosticsAware } from '@push-based/models';
import {
  CssAstVisitor,
  styleAstRuleToSource,
} from '@push-based/styles-ast-utils';

import {
  EXTERNAL_ASSET_ICON,
  INLINE_ASSET_ICON,
  STYLES_ASSET_ICON,
} from './constants.js';
import { ComponentReplacement } from './schema.js';

export type ClassDefinitionVisitor = CssAstVisitor & DiagnosticsAware;

/**
 * Visits a `CssAstVisitor` that is `DiagnosticsAware`and collects the definition of deprecated class names.
 *
 * @example
 * const ast: Root = postcss.parse(`
 *   .btn {
 *     color: red;
 *   }
 * `);
 * const visitor = createClassDefinitionVisitor(ast, { deprecatedCssClasses: ['btn'] });
 * // The visitor will check each `Rule` definition for matching deprecateCssClasses
 * visitEachStyleNode(ast.nodes, visitor);
 *
 * // The visitor is `DiagnosticsAware` and xou can get the issues over a public API.
 * const issues: Issue & { coed?: number } = visitor.getIssues();
 *
 * // Subsequent usags will add to the issues.
 * // You can also clear the issues
 * visitor.clear();
 *
 * @param componentReplacement
 * @param startLine
 */
export const createClassDefinitionVisitor = (
  componentReplacement: ComponentReplacement,
  startLine = 0,
): ClassDefinitionVisitor => {
  const { deprecatedCssClasses = [] } = componentReplacement;
  let diagnostics: Issue[] = [];

  return {
    getIssues(): Issue[] {
      return diagnostics;
    },

    clear(): void {
      diagnostics = [];
    },

    visitRule(rule: Rule) {
      const matchingClassNames = getMatchingClassNames(
        { selector: rule.selector },
        deprecatedCssClasses,
      );

      if (matchingClassNames.length > 0) {
        const message = classUsageMessage({
          className: matchingClassNames.join(', '),
          rule,
          componentName: componentReplacement.componentName,
          docsUrl: componentReplacement.docsUrl,
        });
        const isInline = rule.source?.input.file?.match(/\.ts$/) != null;
        diagnostics.push({
          message,
          severity: 'error',
          source: styleAstRuleToSource(rule, isInline ? startLine : 0),
        });
      }
    },
  };
};

function classUsageMessage({
  className,
  rule,
  componentName,
  docsUrl,
}: Pick<ComponentReplacement, 'componentName' | 'docsUrl'> & {
  className: string;
  rule: Rule;
}): string {
  const isInline = rule.source?.input.file?.match(/\.ts$/) != null;
  const iconString = `${
    isInline ? INLINE_ASSET_ICON : EXTERNAL_ASSET_ICON
  }${STYLES_ASSET_ICON}`;
  const docsLink = docsUrl
    ? ` <a href="${docsUrl}" target="_blank">Learn more</a>.`
    : '';
  return `${iconString}️ The selector's class <code>${className}</code> is deprecated. Use <code>${componentName}</code> and delete the styles.${docsLink}`;
}

export function getMatchingClassNames(
  { selector }: Pick<Rule, 'selector'>,
  targetClassNames: string[],
): string[] {
  const classNames = selector.match(/\.[\w-]+/g) || [];
  return classNames
    .map((className) => className.slice(1)) // Strip the leading "."
    .filter((className) => targetClassNames.includes(className));
}

```

--------------------------------------------------------------------------------
/packages/angular-mcp-server/src/lib/tools/ds/component-contract/shared/models/types.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * Component Contract Types
 */

export type TemplateType = 'inline' | 'external';

export interface ComponentContract {
  meta: Meta;
  publicApi: PublicApi;
  slots: Slots;
  dom: DomStructure;
  styles: StyleDeclarations;
}

export interface Meta {
  name: string;
  selector: string;
  sourceFile: string;
  templateType: TemplateType;
  generatedAt: string;
  hash: string;
}

export interface PublicApi {
  properties: Record<string, PropertyBinding>;
  events: Record<string, EventBinding>;
  methods: Record<string, MethodSignature>;
  lifecycle: string[];
  imports: ImportInfo[];
}

export interface PropertyBinding {
  type: string;
  isInput: boolean;
  required: boolean;
  transform?: string;
}

export interface EventBinding {
  type: string;
}

export interface MethodSignature {
  name: string;
  parameters: ParameterInfo[];
  returnType: string;
  isPublic: boolean;
  isStatic: boolean;
  isAsync: boolean;
}

export interface ParameterInfo {
  name: string;
  type: string;
  optional: boolean;
  defaultValue?: string;
}

export interface ImportInfo {
  name: string;
  path: string;
}

export interface Slots {
  [slotName: string]: {
    selector: string;
  };
}

/**
 * Flat DOM structure with CSS selector keys for efficient lookups and minimal diff noise
 */
export interface DomStructure {
  [selectorKey: string]: DomElement;
}

export interface DomElement {
  tag: string;
  parent: string | null;
  children: string[];
  bindings: Binding[];
  attributes: Attribute[];
  events: Event[];
  /**
   * Optional stack of active structural directives (Angular control-flow / template constructs)
   * that wrap this DOM element, preserving the order of nesting from outermost → innermost.
   * Kept optional so that existing contracts without this field remain valid.
   */
  structural?: StructuralDirectiveContext[];
}

export interface Binding {
  type: 'class' | 'style' | 'property' | 'attribute';
  name: string;
  source: string;
}

export interface Attribute {
  type: 'attribute';
  name: string;
  source: string;
}

export interface Event {
  name: string;
  handler: string;
}

/**
 * Style declarations with explicit DOM relationships for property-level change detection
 */
export interface StyleDeclarations {
  sourceFile: string;
  rules: Record<string, StyleRule>;
}

export interface StyleRule {
  appliesTo: string[];
  properties: Record<string, string>;
}

/**
 * DOM path deduplication utility for diff operations
 */
export interface DomPathDictionary {
  paths: string[];
  lookup: Map<string, number>;
  stats: {
    totalPaths: number;
    uniquePaths: number;
    duplicateReferences: number;
    bytesBeforeDeduplication: number;
    bytesAfterDeduplication: number;
  };
}

/**
 * Captures context of Angular structural directives (v17 control-flow blocks and classic *ngX)
 */
export interface StructuralDirectiveContext {
  kind: 'for' | 'if' | 'switch' | 'switchCase' | 'switchDefault' | 'defer';
  /** Raw expression text (loop expression, condition…) when available */
  expression?: string;
  /** Optional alias / let-var (e.g., let-item) or trackBy identifier */
  alias?: string;
  /** Optional branch inside control-flow blocks (then/else, empty, case…). */
  branch?: 'then' | 'else' | 'empty' | 'case' | 'default';
}

```

--------------------------------------------------------------------------------
/packages/shared/styles-ast-utils/ai/FUNCTIONS.md:
--------------------------------------------------------------------------------

```markdown
# Public API — Quick Reference

| Symbol                 | Kind      | Summary                                            |
| ---------------------- | --------- | -------------------------------------------------- |
| `CssAstVisitor`        | interface | Visitor interface for traversing CSS AST nodes     |
| `NodeType`             | type      | Type mapping for visitor method parameters         |
| `parseStylesheet`      | function  | Parse CSS content and return PostCSS AST           |
| `styleAstRuleToSource` | function  | Convert CSS rule to linkable source location       |
| `stylesAstUtils`       | function  | Utility function (returns package identifier)      |
| `visitEachChild`       | function  | Traverse AST calling visitor methods for each node |
| `visitEachStyleNode`   | function  | Recursively visit CSS nodes with visitor pattern   |
| `visitStyleSheet`      | function  | Visit top-level stylesheet nodes                   |

## Interface Details

### `CssAstVisitor<T = void>`

Visitor interface for processing different types of CSS AST nodes:

- `visitRoot?: (root: Container) => T` - Called once for the root node
- `visitAtRule?: (atRule: AtRule) => T` - Called for @rule nodes (@media, @charset, etc.)
- `visitRule?: (rule: Rule) => T` - Called for CSS rule nodes (.btn, .box, etc.)
- `visitDecl?: (decl: Declaration) => T` - Called for property declarations (color: red, etc.)
- `visitComment?: (comment: Comment) => T` - Called for comment nodes (/_ comment _/)

### `NodeType<K extends keyof CssAstVisitor>`

Type utility that maps visitor method names to their corresponding PostCSS node types:

- `visitRoot` → `Container`
- `visitAtRule` → `AtRule`
- `visitRule` → `Rule`
- `visitDecl` → `Declaration`
- `visitComment` → `Comment`

## Function Details

### `parseStylesheet(content: string, filePath: string)`

Parse CSS content using PostCSS with safe parsing. Returns a PostCSS `LazyResult` object.

**Parameters:**

- `content` - The CSS content to parse
- `filePath` - File path for source mapping and error reporting

**Returns:** PostCSS `LazyResult` with parsed AST

### `styleAstRuleToSource(rule: Pick<Rule, 'source'>, startLine?: number)`

Convert a PostCSS rule to a linkable source location for issue reporting.

**Parameters:**

- `rule` - PostCSS rule with source information
- `startLine` - Optional offset for line numbers (0-indexed, default: 0)

**Returns:** `Issue['source']` object with file path and position information

### `visitEachChild<T>(root: Root, visitor: CssAstVisitor<T>)`

Single function that traverses the entire AST, calling specialized visitor methods for each node type.

**Parameters:**

- `root` - PostCSS Root node to traverse
- `visitor` - Visitor object with optional methods for different node types

### `visitEachStyleNode<T>(nodes: Root['nodes'], visitor: CssAstVisitor<T>)`

Recursively visit CSS nodes using the visitor pattern, processing nested structures.

**Parameters:**

- `nodes` - Array of PostCSS nodes to visit
- `visitor` - Visitor object with optional methods for different node types

### `visitStyleSheet<T>(root: Root, visitor: CssAstVisitor<T>)`

Visit only the top-level nodes of a stylesheet (non-recursive).

**Parameters:**

- `root` - PostCSS Root node
- `visitor` - Visitor object with optional methods for different node types

```

--------------------------------------------------------------------------------
/packages/angular-mcp-server/src/lib/tools/ds/shared/utils/handler-helpers.ts:
--------------------------------------------------------------------------------

```typescript
import {
  CallToolRequest,
  CallToolResult,
} from '@modelcontextprotocol/sdk/types.js';
import { validateComponentName } from './component-validation.js';
import { buildTextResponse, throwError } from './output.utils.js';
import * as process from 'node:process';

/**
 * Common handler options interface - includes both user inputs and MCP server injected config
 */
export interface BaseHandlerOptions {
  cwd?: string;
  directory?: string;
  componentName?: string;
  workspaceRoot?: string;
  // MCP server injected configuration
  storybookDocsRoot?: string;
  deprecatedCssClassesPath?: string;
  uiRoot?: string;
}

/**
 * Handler context with all available configuration
 */
export interface HandlerContext {
  cwd: string;
  workspaceRoot: string;
  storybookDocsRoot?: string;
  deprecatedCssClassesPath?: string;
  uiRoot: string;
}

/**
 * Validates common input parameters
 */
export function validateCommonInputs(params: BaseHandlerOptions): void {
  if (params.componentName) {
    validateComponentName(params.componentName);
  }

  if (params.directory && typeof params.directory !== 'string') {
    throw new Error('Directory parameter is required and must be a string');
  }
}

/**
 * Sets up common environment for handlers
 */
export function setupHandlerEnvironment(
  params: BaseHandlerOptions,
): HandlerContext {
  const originalCwd = process.cwd();
  const cwd = params.cwd || originalCwd;

  if (cwd !== originalCwd) {
    process.chdir(cwd);
  }

  return {
    cwd,
    workspaceRoot: params.workspaceRoot || cwd,
    storybookDocsRoot: params.storybookDocsRoot,
    deprecatedCssClassesPath: params.deprecatedCssClassesPath,
    uiRoot: params.uiRoot || '',
  };
}

/**
 * Generic handler wrapper that provides common functionality
 */
export function createHandler<TParams extends BaseHandlerOptions, TResult>(
  toolName: string,
  handlerFn: (params: TParams, context: HandlerContext) => Promise<TResult>,
  formatResult: (result: TResult) => string[],
) {
  return async (options: CallToolRequest): Promise<CallToolResult> => {
    try {
      const params = options.params.arguments as TParams;

      validateCommonInputs(params);
      const context = setupHandlerEnvironment(params);

      const result = await handlerFn(params, context);
      const formattedLines = formatResult(result);

      return buildTextResponse(formattedLines);
    } catch (ctx) {
      return throwError(`${toolName}: ${(ctx as Error).message || ctx}`);
    }
  };
}

/**
 * Common result formatters
 */
export const RESULT_FORMATTERS = {
  /**
   * Formats a simple success message
   */
  success: (message: string): string[] => [message],

  /**
   * Formats a list of items
   */
  list: (items: string[], title?: string): string[] =>
    title ? [title, ...items.map((item) => `  - ${item}`)] : items,

  /**
   * Formats key-value pairs
   */
  keyValue: (pairs: Record<string, any>): string[] =>
    Object.entries(pairs).map(
      ([key, value]) =>
        `${key}: ${typeof value === 'object' ? JSON.stringify(value, null, 2) : value}`,
    ),

  /**
   * Formats errors with context
   */
  error: (error: string, context?: string): string[] =>
    context ? [`Error in ${context}:`, `  ${error}`] : [`Error: ${error}`],

  /**
   * Formats empty results
   */
  empty: (entityType: string): string[] => [`No ${entityType} found`],
} as const;

```

--------------------------------------------------------------------------------
/packages/angular-mcp-server/src/lib/tools/ds/component-contract/builder/utils/public-api.extractor.ts:
--------------------------------------------------------------------------------

```typescript
import * as ts from 'typescript';
import type { PublicApi } from '../../shared/models/types.js';
import {
  extractClassDeclaration,
  extractPublicMethods,
  extractLifecycleHooks,
  extractImports,
  extractInputsAndOutputs,
} from './typescript-analyzer.js';
import { ParsedComponent } from '@push-based/angular-ast-utils';

type InputMeta = {
  type?: string;
  required?: boolean;
  transform?: string;
};

type OutputMeta = {
  type?: string;
};

/**
 * `ParsedComponent` provided by `angular-ast-utils` does not yet expose
 * `inputs` / `outputs`.  We extend it locally in a non-breaking fashion
 * (both properties remain optional).
 */
type ParsedComponentWithIO = ParsedComponent & {
  inputs?: Record<string, InputMeta>;
  outputs?: Record<string, OutputMeta>;
};

/**
 * Extract Public API from TypeScript class analysis
 */
export function extractPublicApi(
  parsedComponent: ParsedComponentWithIO,
): PublicApi {
  const publicApi: PublicApi = {
    properties: {},
    events: {},
    methods: {},
    lifecycle: [],
    imports: [],
  };

  if (parsedComponent.inputs) {
    for (const [name, config] of Object.entries(
      parsedComponent.inputs as Record<string, InputMeta>,
    )) {
      publicApi.properties[name] = {
        type: config?.type ?? 'any',
        isInput: true,
        required: config?.required ?? false,
        transform: config?.transform,
      };
    }
  }

  if (parsedComponent.outputs) {
    for (const [name, config] of Object.entries(
      parsedComponent.outputs as Record<string, OutputMeta>,
    )) {
      publicApi.events[name] = {
        type: config?.type ?? 'EventEmitter<any>',
      };
    }
  }

  const classDeclaration = extractClassDeclaration(parsedComponent);
  if (classDeclaration) {
    const program = ts.createProgram([parsedComponent.fileName], {
      target: ts.ScriptTarget.Latest,
      module: ts.ModuleKind.ESNext,
      experimentalDecorators: true,
    });
    const sourceFile = program.getSourceFile(parsedComponent.fileName);

    if (sourceFile) {
      const { inputs: allInputs, outputs: allOutputs } =
        extractInputsAndOutputs(classDeclaration, sourceFile);

      for (const [name, config] of Object.entries(allInputs)) {
        const isSignalInput = 'defaultValue' in config || 'transform' in config;

        publicApi.properties[name] = {
          type: config.type ?? 'any',
          isInput: true,
          required: config.required ?? false,
          ...(isSignalInput && {
            transform: (config as any).transform,
            defaultValue: (config as any).defaultValue,
          }),
          ...(!isSignalInput && {
            alias: (config as any).alias,
          }),
        };

        const propRef = publicApi.properties[name] as any;
        if (
          propRef.transform === 'booleanAttribute' &&
          propRef.type === 'any'
        ) {
          propRef.type = 'boolean';
        }
      }

      for (const [name, config] of Object.entries(allOutputs)) {
        publicApi.events[name] = {
          type: config.type ?? 'EventEmitter<any>',
          ...('alias' in config && { alias: config.alias }),
        };
      }

      publicApi.methods = extractPublicMethods(classDeclaration, sourceFile);
      publicApi.lifecycle = extractLifecycleHooks(classDeclaration);
      publicApi.imports = extractImports(sourceFile);
    }
  }

  return publicApi;
}

```

--------------------------------------------------------------------------------
/packages/shared/ds-component-coverage/mocks/fixtures/e2e/demo/code-pushup.config.ts:
--------------------------------------------------------------------------------

```typescript
import { ComponentReplacement } from '../../../../src/lib/runner/audits/ds-coverage/schema';
import { dsComponentUsagePluginCoreConfig } from '../../../../src/core.config.js';
import * as path from 'path';
import { fileURLToPath } from 'url';

const currentDir = path.dirname(fileURLToPath(import.meta.url));
const packageRoot = path.resolve(currentDir, '../../../..');

const dsComponents: ComponentReplacement[] = [
  {
    componentName: 'DSButton',
    deprecatedCssClasses: ['btn', 'btn-primary', 'legacy-button'],
    docsUrl:
      'https://storybook.company.com/latest/?path=/docs/components-button--overview',
  },
  {
    componentName: 'DSTabsModule',
    deprecatedCssClasses: ['ms-tab-bar', 'legacy-tabs', 'custom-tabs'],
    docsUrl:
      'https://storybook.company.com/latest/?path=/docs/components-tabsgroup--overview',
  },
  {
    componentName: 'DSCard',
    deprecatedCssClasses: ['card', 'legacy-card', 'custom-card'],
    docsUrl:
      'https://storybook.company.com/latest/?path=/docs/components-card--overview',
  },
  {
    componentName: 'DSModal',
    deprecatedCssClasses: ['modal', 'popup', 'legacy-dialog'],
    docsUrl:
      'https://storybook.company.com/latest/?path=/docs/components-modal--overview',
  },
  {
    componentName: 'DSInput',
    deprecatedCssClasses: ['input', 'form-control', 'legacy-input'],
    docsUrl:
      'https://storybook.company.com/latest/?path=/docs/components-input--overview',
  },
  {
    componentName: 'DSDropdown',
    deprecatedCssClasses: ['dropdown', 'legacy-dropdown', 'custom-dropdown'],
    docsUrl:
      'https://storybook.company.com/latest/?path=/docs/components-dropdown--overview',
  },
  {
    componentName: 'DSAccordion',
    deprecatedCssClasses: ['accordion', 'collapse-panel', 'legacy-accordion'],
    docsUrl:
      'https://storybook.company.com/latest/?path=/docs/components-accordion--overview',
  },
  {
    componentName: 'DSAlert',
    deprecatedCssClasses: ['alert', 'notification', 'legacy-alert'],
    docsUrl:
      'https://storybook.company.com/latest/?path=/docs/components-alert--overview',
  },
  {
    componentName: 'DSTooltip',
    deprecatedCssClasses: ['tooltip', 'legacy-tooltip', 'info-bubble'],
    docsUrl:
      'https://storybook.company.com/latest/?path=/docs/components-tooltip--overview',
  },
  {
    componentName: 'DSBreadcrumb',
    deprecatedCssClasses: ['breadcrumb', 'legacy-breadcrumb', 'nav-breadcrumb'],
    docsUrl:
      'https://storybook.company.com/latest/?path=/docs/components-breadcrumb--overview',
  },
  {
    componentName: 'DSProgressBar',
    deprecatedCssClasses: ['progress-bar', 'loading-bar', 'legacy-progress'],
    docsUrl:
      'https://storybook.company.com/latest/?path=/docs/components-progressbar--overview',
  },
  {
    componentName: 'DSSlider',
    deprecatedCssClasses: ['slider', 'range-slider', 'legacy-slider'],
    docsUrl:
      'https://storybook.company.com/latest/?path=/docs/components-slider--overview',
  },
  {
    componentName: 'DSNavbar',
    deprecatedCssClasses: ['navbar', 'navigation', 'legacy-navbar'],
    docsUrl: 'https://storybook.company.com/latest/?p',
  },
];

export default {
  persist: {
    outputDir: path.join(
      packageRoot,
      '.code-pushup/ds-component-coverage/demo',
    ),
    format: ['json', 'md'],
  },
  ...(await dsComponentUsagePluginCoreConfig({
    directory: currentDir,
    dsComponents,
  })),
};

```

--------------------------------------------------------------------------------
/packages/angular-mcp-server/src/lib/tools/ds/component-contract/diff/spec/diff-utils.spec.ts:
--------------------------------------------------------------------------------

```typescript
/* eslint-disable prefer-const */
import { describe, it, expect } from 'vitest';

import type { Difference } from 'microdiff';
import {
  consolidateAndPruneRemoveOperations,
  consolidateAndPruneRemoveOperationsWithDeduplication,
  isChildPath,
  groupChangesByDomainAndType,
  generateDiffSummary,
} from '../utils/diff-utils.js';

function makeRemove(path: (string | number)[]): Difference {
  return { type: 'REMOVE', path, oldValue: 'dummy' } as any;
}

function makeAdd(path: (string | number)[], value: any): Difference {
  return { type: 'ADD', path, value } as any;
}

describe('diff-utils', () => {
  describe('isChildPath', () => {
    it('identifies descendant paths correctly', () => {
      expect(isChildPath(['a', 'b', 'c'], ['a', 'b'])).toBe(true);
      expect(isChildPath(['a', 'b'], ['a', 'b', 'c'])).toBe(false);
      expect(isChildPath(['x'], ['x'])).toBe(false);
    });
  });

  describe('consolidateAndPruneRemoveOperations', () => {
    it('consolidates CSS rule removals and prunes redundant child removals', () => {
      const diff: Difference[] = [
        makeRemove(['styles', 'rules', 'div']),
        makeRemove(['styles', 'rules', 'span']),
        makeRemove(['dom', 'elements', 0, 'attributes']),
        makeRemove(['dom', 'elements']),
        makeAdd(['meta', 'name'], 'Foo'),
      ];

      const processed = consolidateAndPruneRemoveOperations(diff);

      expect(processed).toEqual(
        expect.arrayContaining([expect.objectContaining({ type: 'ADD' })]),
      );

      expect(processed).toContainEqual({
        type: 'REMOVE',
        path: ['styles', 'rules'],
        oldValue: ['div', 'span'],
      } as any);

      expect(
        processed.filter((c) => JSON.stringify(c.path).includes('dom')),
      ).toHaveLength(1);
      expect(processed).toContainEqual(makeRemove(['dom', 'elements']));
    });
  });

  describe('consolidateAndPruneRemoveOperationsWithDeduplication', () => {
    it('deduplicates DOM paths into dictionary', () => {
      const LONG_PATH = 'div#root > span.foo > button.bar';

      const diff: Difference[] = [
        {
          type: 'CHANGE',
          path: ['dom', 'elementPath'],
          oldValue: LONG_PATH,
          value: `${LONG_PATH} > svg.icon`,
        } as any,
      ];

      const { processedResult, domPathDict } =
        consolidateAndPruneRemoveOperationsWithDeduplication(diff);

      const refObj = { $domPath: 0 };
      expect((processedResult[0] as any).oldValue).toEqual(refObj);

      expect(domPathDict.paths).toEqual([LONG_PATH, `${LONG_PATH} > svg.icon`]);
      expect(domPathDict.stats.uniquePaths).toBe(2);
    });
  });

  describe('groupChangesByDomainAndType / generateDiffSummary', () => {
    it('groups changes and summarizes stats', () => {
      const diff: Difference[] = [
        makeAdd(['meta', 'name'], 'Foo'),
        makeRemove(['styles', 'rules', 'div']),
      ];

      const grouped = groupChangesByDomainAndType(diff);

      expect(grouped).toHaveProperty('meta');
      expect(grouped.meta).toHaveProperty('ADD');
      expect(grouped.meta.ADD).toHaveLength(1);
      expect(grouped).toHaveProperty('styles');

      const summary = generateDiffSummary(diff, grouped);

      expect(summary.totalChanges).toBe(2);
      expect(summary.changeTypes.ADD).toBe(1);
      expect(summary.changeTypes.REMOVE).toBe(1);
      expect(summary.changesByDomain.meta.ADD).toBe(1);
    });
  });
});

```

--------------------------------------------------------------------------------
/packages/shared/utils/src/lib/file/find-in-file.ts:
--------------------------------------------------------------------------------

```typescript
import { Dirent } from 'node:fs';
import * as fs from 'node:fs/promises';
import * as path from 'node:path';

/**
 * Searches for `.ts` files containing the search pattern.
 * @param {string} baseDir - The directory to search. Should be absolute or resolved by the caller.
 * @param {RegExp | string} searchPattern - The pattern to match.
 */
export async function findFilesWithPattern(
  baseDir: string,
  searchPattern: string,
) {
  const resolvedBaseDir = path.resolve(baseDir);

  const tsFiles: string[] = [];
  for await (const file of findAllFiles(
    resolvedBaseDir,
    (file) => file.endsWith('.ts') && !file.endsWith('.spec.ts'),
  )) {
    tsFiles.push(file);
  }

  const results: SourceLocation[] = [];
  for (const file of tsFiles) {
    try {
      const hits = await findInFile(file, searchPattern);
      if (hits.length > 0) {
        results.push(...hits);
      }
    } catch (ctx) {
      console.error(`Error searching file ${file}:`, ctx);
    }
  }

  return results.map((r: SourceLocation) => r.file);
}

/**
 * Finds all files in a directory and its subdirectories that match a predicate
 */
export async function* findAllFiles(
  baseDir: string,
  predicate: (file: string) => boolean = (fullPath) => fullPath.endsWith('.ts'),
): AsyncGenerator<string> {
  const entries = await getDirectoryEntries(baseDir);

  for (const entry of entries) {
    const fullPath = path.join(baseDir, entry.name);
    if (entry.isDirectory()) {
      // Skip node_modules and other common exclude directories
      if (!isExcludedDirectory(entry.name)) {
        yield* findAllFiles(fullPath, predicate);
      }
    } else if (entry.isFile() && predicate(fullPath)) {
      yield fullPath;
    }
  }
}

export function isExcludedDirectory(fileName: string) {
  return (
    fileName.startsWith('.') ||
    fileName === 'node_modules' ||
    fileName === 'dist' ||
    fileName === 'coverage'
  );
}

async function getDirectoryEntries(dir: string): Promise<Dirent[]> {
  try {
    return await fs.readdir(dir, { withFileTypes: true });
  } catch (ctx) {
    console.error(`Error reading directory ${dir}:`, ctx);
    return [];
  }
}

export function* accessContent(content: string): Generator<string> {
  for (const line of content.split('\n')) {
    yield line;
  }
}

export function getLineHits(
  content: string,
  pattern: string,
  bail = false,
): LinePosition[] {
  const hits: LinePosition[] = [];
  let index = content.indexOf(pattern);

  while (index !== -1) {
    hits.push({ startColumn: index, endColumn: index + pattern.length });
    if (bail) {
      return hits;
    }
    index = content.indexOf(pattern, index + 1);
  }
  return hits;
}

export type LinePosition = {
  startColumn: number;
  endColumn?: number;
};

export type SourcePosition = {
  startLine: number;
  endLine?: number;
} & LinePosition;

export type SourceLocation = {
  file: string;
  position: SourcePosition;
};

export async function findInFile(
  file: string,
  searchPattern: string,
  bail = false,
): Promise<SourceLocation[]> {
  const hits: SourceLocation[] = [];
  const content = await fs.readFile(file, 'utf8');
  let startLine = 0;
  for (const line of accessContent(content)) {
    startLine++;
    getLineHits(line, searchPattern, bail).forEach((position) => {
      hits.push({
        file,
        position: {
          startLine,
          ...position,
        },
      });
    });
  }
  return hits;
}

```

--------------------------------------------------------------------------------
/packages/angular-mcp-server/src/lib/tools/ds/component-contract/list/list-component-contracts.tool.ts:
--------------------------------------------------------------------------------

```typescript
import { readdir, stat, readFile } from 'node:fs/promises';
import { join } from 'node:path';
import {
  createHandler,
  BaseHandlerOptions,
} from '../../shared/utils/handler-helpers.js';
import { resolveCrossPlatformPath } from '../../shared/utils/cross-platform-path.js';
import { listComponentContractsSchema } from './models/schema.js';
import type { ContractFileInfo } from './models/types.js';
import {
  extractComponentNameFromFile,
  formatBytes,
  formatContractsByComponent,
} from './utils/contract-list-utils.js';

interface ListComponentContractsOptions extends BaseHandlerOptions {
  directory: string;
}

/**
 * Recursively scan directory for contract files
 */
async function scanContractsRecursively(
  dirPath: string,
  contracts: ContractFileInfo[],
): Promise<void> {
  try {
    const entries = await readdir(dirPath, { withFileTypes: true });

    for (const entry of entries) {
      const fullPath = join(dirPath, entry.name);

      if (entry.isDirectory()) {
        // Recursively scan subdirectories
        await scanContractsRecursively(fullPath, contracts);
      } else if (entry.isFile() && entry.name.endsWith('.contract.json')) {
        // Process contract file
        const stats = await stat(fullPath);

        try {
          const contractData = JSON.parse(await readFile(fullPath, 'utf-8'));
          const componentName =
            contractData.metadata?.componentName ||
            extractComponentNameFromFile(entry.name);

          contracts.push({
            fileName: entry.name,
            filePath: fullPath,
            componentName,
            timestamp:
              contractData.metadata?.timestamp || stats.mtime.toISOString(),
            hash: contractData.hash || 'unknown',
            size: formatBytes(stats.size),
          });
        } catch {
          console.warn(`Skipping invalid contract file: ${fullPath}`);
          continue;
        }
      }
    }
  } catch (ctx) {
    // Silently skip directories that can't be read
    if ((ctx as NodeJS.ErrnoException).code !== 'ENOENT') {
      console.warn(`Error scanning directory ${dirPath}:`, ctx);
    }
  }
}

export const listComponentContractsHandler = createHandler<
  ListComponentContractsOptions,
  ContractFileInfo[]
>(
  listComponentContractsSchema.name,
  async (_, { cwd: _cwd, workspaceRoot }) => {
    const contractDir = resolveCrossPlatformPath(
      workspaceRoot,
      '.cursor/tmp/contracts',
    );
    const contracts: ContractFileInfo[] = [];

    await scanContractsRecursively(contractDir, contracts);

    // Sort by timestamp (newest first)
    contracts.sort(
      (a, b) =>
        new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime(),
    );

    return contracts;
  },
  (contracts) => {
    if (contracts.length === 0) {
      return [
        '📁 No component contracts found',
        '💡 Use the build_component_contract tool to generate contracts',
        '🎯 Contracts are stored in .cursor/tmp/contracts/*.contract.json',
      ];
    }

    const output: string[] = [];

    output.push(...formatContractsByComponent(contracts));

    output.push('💡 Use diff_component_contract to compare contracts');
    output.push('🔄 Newer contracts appear first within each component');

    return output;
  },
);

export const listComponentContractsTools = [
  {
    schema: listComponentContractsSchema,
    handler: listComponentContractsHandler,
  },
];

```

--------------------------------------------------------------------------------
/packages/angular-mcp-server/src/lib/tools/ds/report-violations/report-all-violations.tool.ts:
--------------------------------------------------------------------------------

```typescript
import {
  BaseHandlerOptions,
  createHandler,
} from '../shared/utils/handler-helpers.js';
import {
  COMMON_ANNOTATIONS,
  createProjectAnalysisSchema,
} from '../shared/models/schema-helpers.js';
import {
  analyzeProjectCoverage,
  extractComponentName,
} from '../shared/violation-analysis/coverage-analyzer.js';
import {
  formatViolations,
  filterFailedAudits,
  groupIssuesByFile,
} from '../shared/violation-analysis/formatters.js';
import { loadAndValidateDsComponentsFile } from '../../../validation/ds-components-file-loader.validation.js';
import { RESULT_FORMATTERS } from '../shared/utils/handler-helpers.js';

interface ReportAllViolationsOptions extends BaseHandlerOptions {
  directory: string;
  groupBy?: 'file' | 'folder';
}

export const reportAllViolationsSchema = {
  name: 'report-all-violations',
  description:
    'Scan a directory for deprecated design system CSS classes defined in the config at `deprecatedCssClassesPath`, and output a usage report',
  inputSchema: createProjectAnalysisSchema({
    groupBy: {
      type: 'string',
      enum: ['file', 'folder'],
      description: 'How to group the results',
      default: 'file',
    },
  }),
  annotations: {
    title: 'Report All Violations',
    ...COMMON_ANNOTATIONS.readOnly,
  },
};

export const reportAllViolationsHandler = createHandler<
  ReportAllViolationsOptions,
  string[]
>(
  reportAllViolationsSchema.name,
  async (params, { cwd, deprecatedCssClassesPath }) => {
    if (!deprecatedCssClassesPath) {
      throw new Error(
        'Missing ds.deprecatedCssClassesPath. Provide --ds.deprecatedCssClassesPath in mcp.json file.',
      );
    }
    const groupBy = params.groupBy || 'file';
    const dsComponents = await loadAndValidateDsComponentsFile(
      cwd,
      deprecatedCssClassesPath || '',
    );

    const coverageResult = await analyzeProjectCoverage({
      cwd,
      returnRawData: true,
      directory: params.directory,
      dsComponents,
    });

    const raw = coverageResult.rawData?.rawPluginResult;
    if (!raw) return [];

    const failedAudits = filterFailedAudits(raw);
    if (failedAudits.length === 0) return ['No violations found.'];

    if (groupBy === 'file') {
      const lines: string[] = [];
      for (const audit of failedAudits) {
        extractComponentName(audit.title);
        const fileGroups = groupIssuesByFile(
          audit.details?.issues ?? [],
          params.directory,
        );
        for (const [fileName, { lines: fileLines, message }] of Object.entries(
          fileGroups,
        )) {
          const sorted =
            fileLines.length > 1
              ? [...fileLines].sort((a, b) => a - b)
              : fileLines;
          const lineInfo =
            sorted.length > 1
              ? `lines ${sorted.join(', ')}`
              : `line ${sorted[0]}`;
          lines.push(`${fileName} (${lineInfo}): ${message}`);
        }
      }
      return lines;
    }

    const formattedContent = formatViolations(raw, params.directory, {
      groupBy: 'folder',
    });
    return formattedContent.map(
      (item: { type?: string; text?: string } | string) =>
        typeof item === 'string' ? item : (item?.text ?? String(item)),
    );
  },
  (result) => RESULT_FORMATTERS.list(result, 'Design System Violations:'),
);

export const reportAllViolationsTools = [
  {
    schema: reportAllViolationsSchema,
    handler: reportAllViolationsHandler,
  },
];

```

--------------------------------------------------------------------------------
/packages/angular-mcp-server/src/lib/tools/ds/component-contract/builder/spec/element-helpers.spec.ts:
--------------------------------------------------------------------------------

```typescript
import { describe, it, expect } from 'vitest';

import {
  extractBindings,
  extractAttributes,
  extractEvents,
} from '../utils/element-helpers.js';

type MockASTWithSource = { source: string };

type MockPosition = {
  offset: number;
  file: { url: string };
};

type MockSourceSpan = {
  start: MockPosition;
  end: MockPosition;
};

function makeSourceSpan(
  start: number,
  end: number,
  fileUrl = '/comp.html',
): MockSourceSpan {
  return {
    start: { offset: start, file: { url: fileUrl } },
    end: { offset: end, file: { url: fileUrl } },
  };
}

interface MockInput {
  name: string;
  value: MockASTWithSource;
  sourceSpan?: MockSourceSpan;
}

interface MockAttribute {
  name: string;
  value: string;
}

interface MockOutput {
  name: string;
  handler: MockASTWithSource;
}

interface MockElement {
  inputs: MockInput[];
  attributes: MockAttribute[];
  outputs: MockOutput[];
}

function createElement(partial: Partial<MockElement>): MockElement {
  return {
    inputs: [],
    attributes: [],
    outputs: [],
    ...partial,
  } as MockElement;
}

describe('element-helpers', () => {
  describe('extractBindings', () => {
    it('class, style, attribute, and property binding types are detected', () => {
      const element = createElement({
        inputs: [
          { name: 'class.foo', value: { source: 'cond' } },
          { name: 'style.color', value: { source: 'expr' } },
          { name: 'attr.data-id', value: { source: 'id' } },
          { name: 'value', value: { source: 'val' } },
        ],
      });

      const bindings = extractBindings(element as any);

      expect(bindings).toEqual([
        {
          type: 'class',
          name: 'class.foo',
          source: 'cond',
          sourceSpan: undefined,
        },
        {
          type: 'style',
          name: 'style.color',
          source: 'expr',
          sourceSpan: undefined,
        },
        {
          type: 'attribute',
          name: 'attr.data-id',
          source: 'id',
          sourceSpan: undefined,
        },
        {
          type: 'property',
          name: 'value',
          source: 'val',
          sourceSpan: undefined,
        },
      ]);
    });

    it('maps sourceSpan information', () => {
      const span = makeSourceSpan(5, 15);
      const element = createElement({
        inputs: [
          { name: 'value', value: { source: 'expr' }, sourceSpan: span },
        ],
      });

      const [binding] = extractBindings(element as any);
      expect(binding.sourceSpan).toEqual({
        start: 5,
        end: 15,
        file: '/comp.html',
      });
    });
  });

  describe('extractAttributes', () => {
    it('returns attribute objects with type="attribute"', () => {
      const element = createElement({
        attributes: [
          { name: 'id', value: 'root' },
          { name: 'role', value: 'banner' },
        ],
      });

      const attrs = extractAttributes(element as any);
      expect(attrs).toEqual([
        { type: 'attribute', name: 'id', source: 'root' },
        { type: 'attribute', name: 'role', source: 'banner' },
      ]);
    });
  });

  describe('extractEvents', () => {
    it('returns event objects with handler source', () => {
      const element = createElement({
        outputs: [{ name: 'click', handler: { source: 'onClick()' } }],
      });

      const events = extractEvents(element as any);
      expect(events).toEqual([{ name: 'click', handler: 'onClick()' }]);
    });
  });
});

```

--------------------------------------------------------------------------------
/packages/minimal-repo/packages/application/src/app/components/refactoring-tests/complex-components/second-case/complex-widget-demo.component.ts:
--------------------------------------------------------------------------------

```typescript
import { Component, signal } from '@angular/core';
import { ComplexBadgeWidgetComponent, BadgeConfig } from './complex-badge-widget.component';

@Component({
  selector: 'app-complex-widget-demo',
  standalone: true,
  imports: [ComplexBadgeWidgetComponent],
  template: `
    <div class="demo-container">
      <h2>Complex Badge Widget Demo</h2>
      <p>This component demonstrates a complex badge implementation that will fail when refactored to DsBadge.</p>
      
      <app-complex-badge-widget
        [initialBadges]="customBadges()"
        (badgeSelected)="onBadgeSelected($event)"
        (badgeModified)="onBadgeModified($event)"
        (badgeDeleted)="onBadgeDeleted($event)">
      </app-complex-badge-widget>
      
      <div class="demo-log">
        <h3>Event Log:</h3>
        <div class="log-entries">
          @for (entry of eventLog(); track $index) {
            <div class="log-entry">{{ entry }}</div>
          }
        </div>
      </div>
    </div>
  `,
  styles: [`
    .demo-container {
      padding: 2rem;
      max-width: 1200px;
      margin: 0 auto;
    }
    
    .demo-container h2 {
      color: #1f2937;
      margin-bottom: 1rem;
    }
    
    .demo-container p {
      color: #6b7280;
      margin-bottom: 2rem;
    }
    
    .demo-log {
      background: white;
      border-radius: 0.5rem;
      padding: 1.5rem;
      box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1);
      margin-top: 2rem;
    }
    
    .demo-log h3 {
      margin: 0 0 1rem 0;
      color: #1f2937;
      font-size: 1.125rem;
    }
    
    .log-entries {
      max-height: 200px;
      overflow-y: auto;
    }
    
    .log-entry {
      padding: 0.5rem;
      border-bottom: 1px solid #f3f4f6;
      font-size: 0.875rem;
      color: #374151;
      font-family: monospace;
    }
    
    .log-entry:last-child {
      border-bottom: none;
    }
  `]
})
export class ComplexWidgetDemoComponent {
  eventLog = signal<string[]>([]);
  
  customBadges = signal<BadgeConfig[]>([
    {
      id: 'complex-1',
      text: 'Ultra Premium',
      type: 'offer-badge',
      level: 'critical',
      interactive: true,
      customData: { 
        prop: 'ultra-premium', 
        complexity: 'high',
        features: ['animations', 'tooltips', 'dom-manipulation'],
        breakingPoints: ['nested-structure', 'pseudo-elements', 'direct-dom-access']
      }
    },
    {
      id: 'complex-2',
      text: 'System Health',
      type: 'status',
      level: 'high',
      interactive: false,
      customData: { 
        prop: 'health-monitor',
        realTime: true,
        dependencies: ['custom-animations', 'complex-selectors'],
        refactorRisk: 'high'
      }
    },
    {
      id: 'complex-3',
      text: 'Critical Priority',
      type: 'priority',
      level: 'critical',
      interactive: true,
      customData: { 
        prop: 'priority-alert',
        escalated: true,
        customBehaviors: ['hover-effects', 'click-handlers', 'tooltip-system'],
        migrationComplexity: 'very-high'
      }
    }
  ]);

  onBadgeSelected(badgeId: string) {
    this.addLogEntry(`Badge selected: ${badgeId}`);
  }

  onBadgeModified(badge: BadgeConfig) {
    this.addLogEntry(`Badge modified: ${badge.id} - ${badge.text}`);
  }

  onBadgeDeleted(badgeId: string) {
    this.addLogEntry(`Badge deleted: ${badgeId}`);
  }

  private addLogEntry(message: string) {
    const timestamp = new Date().toLocaleTimeString();
    const entry = `[${timestamp}] ${message}`;
    this.eventLog.update(log => [entry, ...log.slice(0, 49)]);
  }
} 
```

--------------------------------------------------------------------------------
/packages/minimal-repo/packages/application/src/app/components/validation-tests/valid.component.ts:
--------------------------------------------------------------------------------

```typescript
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';

@Component({
  selector: 'app-valid',
  standalone: true,
  imports: [CommonModule, FormsModule],
  template: `
    <div class="valid-component">
      <h2>{{ title }}</h2>
      <div class="todo-section">
        <input 
          [(ngModel)]="newTodo" 
          placeholder="Enter a new todo"
          (keyup.enter)="addTodo()"
          class="todo-input"
        />
        <button (click)="addTodo()" [disabled]="!newTodo.trim()">
          Add Todo
        </button>
      </div>
      
      <ul class="todo-list" *ngIf="todos.length > 0">
        <li *ngFor="let todo of todos; let i = index" class="todo-item">
          <span [class.completed]="todo.completed">{{ todo.text }}</span>
          <div class="todo-actions">
            <button (click)="toggleTodo(i)" class="toggle-btn">
              {{ todo.completed ? 'Undo' : 'Complete' }}
            </button>
            <button (click)="removeTodo(i)" class="remove-btn">Remove</button>
          </div>
        </li>
      </ul>
      
      <div class="stats" *ngIf="todos.length > 0">
        <p>Total: {{ todos.length }} | 
           Completed: {{ completedCount }} | 
           Remaining: {{ remainingCount }}
        </p>
      </div>
    </div>
  `,
  styles: [`
    .valid-component {
      max-width: 500px;
      margin: 20px auto;
      padding: 20px;
      border: 2px solid #4CAF50;
      border-radius: 8px;
      background: #f9fff9;
    }
    
    .todo-section {
      display: flex;
      gap: 10px;
      margin-bottom: 20px;
    }
    
    .todo-input {
      flex: 1;
      padding: 8px;
      border: 1px solid #ddd;
      border-radius: 4px;
    }
    
    .todo-list {
      list-style: none;
      padding: 0;
    }
    
    .todo-item {
      display: flex;
      justify-content: space-between;
      align-items: center;
      padding: 10px;
      margin-bottom: 5px;
      background: white;
      border-radius: 4px;
      border: 1px solid #eee;
    }
    
    .completed {
      text-decoration: line-through;
      color: #888;
    }
    
    .todo-actions {
      display: flex;
      gap: 5px;
    }
    
    .toggle-btn, .remove-btn {
      padding: 4px 8px;
      border: none;
      border-radius: 3px;
      cursor: pointer;
      font-size: 12px;
    }
    
    .toggle-btn {
      background: #2196F3;
      color: white;
    }
    
    .remove-btn {
      background: #f44336;
      color: white;
    }
    
    .stats {
      margin-top: 20px;
      padding: 10px;
      background: #e8f5e8;
      border-radius: 4px;
      text-align: center;
    }
  `]
})
export class ValidComponent {
  title = 'Valid Todo Component';
  newTodo = '';
  todos: { text: string; completed: boolean }[] = [
    { text: 'Learn Angular', completed: true },
    { text: 'Build awesome apps', completed: false }
  ];

  addTodo(): void {
    if (this.newTodo.trim()) {
      this.todos.push({
        text: this.newTodo.trim(),
        completed: false
      });
      this.newTodo = '';
    }
  }

  toggleTodo(index: number): void {
    if (index >= 0 && index < this.todos.length) {
      this.todos[index].completed = !this.todos[index].completed;
    }
  }

  removeTodo(index: number): void {
    if (index >= 0 && index < this.todos.length) {
      this.todos.splice(index, 1);
    }
  }

  get completedCount(): number {
    return this.todos.filter(todo => todo.completed).length;
  }

  get remainingCount(): number {
    return this.todos.filter(todo => !todo.completed).length;
  }
} 
```

--------------------------------------------------------------------------------
/packages/angular-mcp-server/src/lib/tools/ds/component-contract/shared/utils/contract-file-ops.ts:
--------------------------------------------------------------------------------

```typescript
import { readFile, mkdir, writeFile } from 'node:fs/promises';
import { existsSync } from 'node:fs';
import { resolve, basename, extname } from 'node:path';
import { createHash } from 'node:crypto';
import { ComponentContract } from '../models/types.js';
import { resolveCrossPlatformPath } from '../../../shared/utils/cross-platform-path.js';
import { componentNameToKebabCase } from '../../../shared/utils/component-validation.js';

/**
 * Load a contract from a JSON file, handling both wrapped and direct formats
 */
export async function loadContract(path: string): Promise<ComponentContract> {
  if (!existsSync(path)) {
    throw new Error(`Contract file not found: ${path}`);
  }

  const content = await readFile(path, 'utf-8');
  const data = JSON.parse(content);

  return data.contract || data;
}

/**
 * Save a contract to the standard location with metadata
 */
export async function saveContract(
  contract: ComponentContract,
  workspaceRoot: string,
  templatePath: string,
  scssPath: string,
  cwd: string,
  dsComponentName?: string,
): Promise<{ contractFilePath: string; hash: string }> {
  const componentName = basename(templatePath, extname(templatePath));

  // Stringify early so we can compute a deterministic hash before naming the file
  const contractString = JSON.stringify(contract, null, 2);

  const hash = createHash('sha256').update(contractString).digest('hex');

  const timestamp = new Date()
    .toISOString()
    .replace(/[-:]/g, '')
    .replace(/\.\d+Z$/, 'Z');

  const contractFileName = `${componentName}-${timestamp}.contract.json`;

  // Determine final directory: .cursor/tmp/contracts/<kebab-scope>
  let contractDir = resolveCrossPlatformPath(
    workspaceRoot,
    '.cursor/tmp/contracts',
  );

  if (dsComponentName) {
    const folderSlug = componentNameToKebabCase(dsComponentName);
    contractDir = resolveCrossPlatformPath(
      workspaceRoot,
      `.cursor/tmp/contracts/${folderSlug}`,
    );
  }

  await mkdir(contractDir, { recursive: true });

  const contractFilePath = resolve(contractDir, contractFileName);

  const contractData = {
    contract,
    hash: `sha256-${hash}`,
    metadata: {
      templatePath: resolve(cwd, templatePath),
      scssPath: resolve(cwd, scssPath),
      timestamp: new Date().toISOString(),
      componentName,
    },
  };

  await writeFile(
    contractFilePath,
    JSON.stringify(contractData, null, 2),
    'utf-8',
  );

  return {
    contractFilePath,
    hash: `sha256-${hash}`,
  };
}

/**
 * Generate a standardized contract summary for display
 */
export function generateContractSummary(contract: ComponentContract): string[] {
  return [
    `🎯 DOM Elements: ${Object.keys(contract.dom).length}`,
    `🎨 Style Rules: ${Object.keys(contract.styles.rules).length}`,
    `📥 Properties: ${Object.keys(contract.publicApi.properties).length}`,
    `📤 Events: ${Object.keys(contract.publicApi.events).length}`,
    `⚙️  Methods: ${Object.keys(contract.publicApi.methods).length}`,
    `🔄 Lifecycle Hooks: ${contract.publicApi.lifecycle.length}`,
    `📦 Imports: ${contract.publicApi.imports.length}`,
    `🎪 Slots: ${Object.keys(contract.slots).length}`,
    `📁 Source: ${contract.meta.sourceFile}`,
  ];
}

/**
 * Generate a timestamped filename for diff results
 */
export function generateDiffFileName(
  beforePath: string,
  afterPath: string,
): string {
  const beforeName = basename(beforePath, '.contract.json');
  const afterName = basename(afterPath, '.contract.json');
  const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
  return `diff-${beforeName}-vs-${afterName}-${timestamp}.json`;
}

```

--------------------------------------------------------------------------------
/docs/writing-custom-tools.md:
--------------------------------------------------------------------------------

```markdown
# Writing Custom Tools for Angular MCP Server

> **Goal:** Enable developers to add new, type-safe MCP tools that can be called by LLMs or scripts.

---

## 1. Anatomy of a Tool

Every tool consists of **three parts**:

1. **Schema** – JSON-schema (via `zod`) that describes the tool name, description and arguments.
2. **Handler** – an async function that receives a typed `CallToolRequest`, executes logic, and returns a `CallToolResult`.
3. **Registration** – export a `ToolsConfig` object and add it to the category list (`dsTools`, etc.).

Directory convention
```
packages/angular-mcp-server/src/lib/tools/<category>/my-feature/hello-world.tool.ts
```
File name **must** end with `.tool.ts` so tests & registries can auto-discover it.

---

## 2. Boilerplate Template

```ts
import { z } from 'zod';
import { ToolSchemaOptions, ToolsConfig } from '@push-based/models';
import { createHandler, RESULT_FORMATTERS } from '../shared/utils/handler-helpers.js';

// 1️⃣ Schema
const helloWorldSchema: ToolSchemaOptions = {
  name: 'hello-world',
  description: 'Echo a friendly greeting',
  inputSchema: z.object({
    name: z.string().describe('Name to greet'),
  }),
  annotations: {
    title: 'Hello World',
  },
};

// 2️⃣ Handler (business logic)
const helloWorldHandler = createHandler<{ name: string }, string>(
  helloWorldSchema.name,
  async (params) => {
    return `Hello, ${params.name}! 👋`;
  },
  (result) => RESULT_FORMATTERS.success(result),
);

// 3️⃣ Registration
export const helloWorldTools: ToolsConfig[] = [
  { schema: helloWorldSchema, handler: helloWorldHandler },
];
```

Key points:
* `createHandler` automatically validates common arguments, injects workspace paths, and formats output.
* Generic parameters `<{ name: string }, string>` indicate input shape and raw result type.
* Use `RESULT_FORMATTERS` helpers to produce consistent textual arrays.

---

## 3. Adding to the Registry

Open the category file (e.g. `tools/ds/tools.ts`) and spread your array:

```ts
import { helloWorldTools } from './my-feature/hello-world.tool';

export const dsTools: ToolsConfig[] = [
  // …existing
  ...helloWorldTools,
];
```

The server will now expose `hello-world` via `list_tools`.

---

## 4. Parameter Injection

The server adds workspace-specific paths to every call so you don’t need to pass them manually:

| Field | Injected Value |
|-------|----------------|
| `cwd` | Current working dir (may be overridden) |
| `workspaceRoot` | `--workspaceRoot` CLI flag |
| `storybookDocsRoot` | Relative path from CLI flags |
| `deprecatedCssClassesPath` | Path to deprecated CSS map |
| `uiRoot` | Path to DS component source |

Access them inside the handler via the second argument of `createHandler`:

```ts
async (params, ctx) => {
  console.log('Workspace root:', ctx.workspaceRoot);
}
```

---

## 5. Validation Helpers

* `validateCommonInputs` – ensures `directory` is string, `componentName` matches `Ds[A-Z]…` pattern.
* Custom validation: extend the Zod `inputSchema` with additional constraints.

---

## 6. Testing Your Tool

1. **Unit tests** (recommended): import the handler function directly and assert on the returned `CallToolResult`.

---

## 7. Documentation Checklist

After publishing a tool:

- [ ] Add an entry to `docs/tools.md` with purpose, parameters, output.

---

## 8. Common Pitfalls

| Pitfall | Fix |
|---------|-----|
| Tool not listed via `list_tools` | Forgot to spread into `dsTools` array. |
| “Unknown argument” error | Ensure `inputSchema` matches argument names exactly. |
| Long-running sync FS operations | Use async `fs/promises` or worker threads to keep server responsive. |

---

Happy tooling! 🎉 
```

--------------------------------------------------------------------------------
/packages/angular-mcp-server/src/lib/tools/ds/shared/utils/regex-helpers.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * Shared regex utilities for DS tools
 * Consolidates regex patterns used across multiple tools to avoid duplication
 */

// CSS Processing Regexes
export const CSS_REGEXES = {
  /**
   * Escapes special regex characters in a string for safe use in regex patterns
   */
  escape: (str: string): string => str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'),

  /**
   * Creates a regex to match CSS classes from a list of class names
   */
  createClassMatcher: (classNames: string[]): RegExp =>
    new RegExp(
      `\\.(${classNames.map(CSS_REGEXES.escape).join('|')})(?![\\w-])`,
      'g',
    ),

  /**
   * Style file extensions
   */
  STYLE_EXTENSIONS: /\.(css|scss|sass|less)$/i,
} as const;

// Path Processing Regexes
export const PATH_REGEXES = {
  /**
   * Normalizes file paths to use forward slashes
   */
  normalizeToUnix: (path: string): string => path.replace(/\\/g, '/'),

  /**
   * Removes directory prefix from file paths
   */
  removeDirectoryPrefix: (filePath: string, directory: string): string => {
    const normalizedFilePath = PATH_REGEXES.normalizeToUnix(filePath);
    const normalizedDirectory = PATH_REGEXES.normalizeToUnix(directory);
    const directoryPrefix = normalizedDirectory.endsWith('/')
      ? normalizedDirectory
      : normalizedDirectory + '/';

    return normalizedFilePath.startsWith(directoryPrefix)
      ? normalizedFilePath.replace(directoryPrefix, '')
      : normalizedFilePath;
  },
} as const;

// Component Name Processing Regexes
export const COMPONENT_REGEXES = {
  /**
   * Converts DS component name to kebab-case
   */
  toKebabCase: (componentName: string): string =>
    componentName
      .replace(/^Ds/, '')
      .replace(/([a-z0-9])([A-Z])/g, '$1-$2')
      .toLowerCase(),

  /**
   * Validates DS component name format (accepts both "DsButton" and "Button" formats)
   */
  isValidDsComponent: (name: string): boolean =>
    /^(Ds)?[A-Z][a-zA-Z0-9]*$/.test(name),

  /**
   * Extracts component name from coverage titles
   */
  extractFromCoverageTitle: (title: string): string | null => {
    const match = title.match(/Usage coverage for (\w+) component/);
    return match ? match[1] : null;
  },
} as const;

// Import/Dependency Processing Regexes (from component-usage-graph)
export const IMPORT_REGEXES = {
  ES6_IMPORT:
    /import\s+(?:(?:[\w\s{},*]+\s+from\s+)?['"`]([^'"`]+)['"`]|['"`]([^'"`]+)['"`])/g,
  COMMONJS_REQUIRE: /require\s*\(\s*['"`]([^'"`]+)['"`]\s*\)/g,
  DYNAMIC_IMPORT: /import\s*\(\s*['"`]([^'"`]+)['"`]\s*\)/g,
  CSS_IMPORT: /@import\s+['"`]([^'"`]+)['"`]/g,
  CSS_URL: /url\s*\(\s*['"`]?([^'"`)]+)['"`]?\s*\)/g,
  ANGULAR_COMPONENT_DECORATOR: /@Component/,

  /**
   * Creates cached regex for component imports
   */
  createComponentImportRegex: (componentName: string): RegExp =>
    new RegExp(`import[\\s\\S]*?\\b${componentName}\\b[\\s\\S]*?from`, 'gm'),

  /**
   * Creates combined regex for multiple component imports
   */
  createCombinedComponentImportRegex: (componentNames: string[]): RegExp =>
    new RegExp(
      `import[\\s\\S]*?\\b(${componentNames.join('|')})\\b[\\s\\S]*?from`,
      'gm',
    ),
} as const;

// Regex Cache Management
const REGEX_CACHE = new Map<string, RegExp>();

export const REGEX_CACHE_UTILS = {
  /**
   * Gets or creates a cached regex
   */
  getOrCreate: (key: string, factory: () => RegExp): RegExp => {
    let regex = REGEX_CACHE.get(key);
    if (!regex) {
      regex = factory();
      REGEX_CACHE.set(key, regex);
    }
    regex.lastIndex = 0; // Reset for consistent behavior
    return regex;
  },

  /**
   * Clears the regex cache
   */
  clear: (): void => REGEX_CACHE.clear(),

  /**
   * Gets cache statistics
   */
  getStats: () => ({ size: REGEX_CACHE.size }),
} as const;

```

--------------------------------------------------------------------------------
/packages/shared/ds-component-coverage/ai/EXAMPLES.md:
--------------------------------------------------------------------------------

```markdown
# Examples

## 1 — Basic plugin setup

> Create a DS component coverage plugin to detect deprecated CSS classes.

```ts
import { dsComponentCoveragePlugin } from '@push-based/ds-component-coverage';

const plugin = dsComponentCoveragePlugin({
  directory: './src/app',
  dsComponents: [
    {
      componentName: 'DsButton',
      deprecatedCssClasses: ['btn', 'button-primary'],
      docsUrl: 'https://design-system.com/button',
    },
  ],
});

console.log(plugin.slug); // → 'ds-component-coverage'
```

---

## 2 — Running coverage analysis

> Execute the runner function to analyze Angular components for deprecated CSS usage.

```ts
import { runnerFunction } from '@push-based/ds-component-coverage';

const results = await runnerFunction({
  directory: './src/app',
  dsComponents: [
    {
      componentName: 'DsCard',
      deprecatedCssClasses: ['card', 'card-header', 'card-body'],
    },
  ],
});

console.log(results.length); // → Number of audit outputs
```

---

## 3 — Multiple component tracking

> Track multiple design system components and their deprecated classes.

```ts
import { dsComponentCoveragePlugin } from '@push-based/ds-component-coverage';

const plugin = dsComponentCoveragePlugin({
  directory: './src',
  dsComponents: [
    {
      componentName: 'DsButton',
      deprecatedCssClasses: ['btn', 'button'],
      docsUrl: 'https://design-system.com/button',
    },
    {
      componentName: 'DsModal',
      deprecatedCssClasses: ['modal', 'dialog'],
      docsUrl: 'https://design-system.com/modal',
    },
    {
      componentName: 'DsInput',
      deprecatedCssClasses: ['form-control', 'input-field'],
    },
  ],
});

console.log(plugin.audits.length); // → 3 audits (one per component)
```

---

## 4 — Code Pushup integration

> Integrate with Code Pushup for automated design system migration tracking.

```ts
import {
  dsComponentCoveragePlugin,
  getAngularDsUsageCategoryRefs,
} from '@push-based/ds-component-coverage';

const dsComponents = [
  {
    componentName: 'DsBadge',
    deprecatedCssClasses: ['badge', 'label'],
    docsUrl: 'https://design-system.com/badge',
  },
];

// Use in code-pushup.config.ts
export default {
  plugins: [
    dsComponentCoveragePlugin({
      directory: './src/app',
      dsComponents,
    }),
  ],
  categories: [
    {
      slug: 'design-system-usage',
      title: 'Design System Usage',
      description: 'Usage of design system components',
      refs: getAngularDsUsageCategoryRefs(dsComponents),
    },
  ],
};
```

---

## 5 — Category references for reporting

> Generate category references for organizing audit results.

```ts
import { getAngularDsUsageCategoryRefs } from '@push-based/ds-component-coverage';

const dsComponents = [
  {
    componentName: 'DsButton',
    deprecatedCssClasses: ['btn'],
  },
  {
    componentName: 'DsCard',
    deprecatedCssClasses: ['card'],
  },
];

const categoryRefs = getAngularDsUsageCategoryRefs(dsComponents);
console.log(categoryRefs); // → Array of category references for each component
```

---

## 6 — Custom configuration with schema validation

> Use schema validation to ensure proper configuration structure.

```ts
import {
  ComponentCoverageRunnerOptionsSchema,
  ComponentReplacementSchema,
} from '@push-based/ds-component-coverage';

// Validate individual component replacement
const componentConfig = ComponentReplacementSchema.parse({
  componentName: 'DsAlert',
  deprecatedCssClasses: ['alert', 'notification'],
  docsUrl: 'https://design-system.com/alert',
});

// Validate full runner options
const runnerConfig = ComponentCoverageRunnerOptionsSchema.parse({
  directory: './src/app',
  dsComponents: [componentConfig],
});

console.log(runnerConfig.directory); // → './src/app'
```

```

--------------------------------------------------------------------------------
/packages/shared/utils/src/lib/execute-process.unit.test.ts:
--------------------------------------------------------------------------------

```typescript
import { ChildProcess } from 'node:child_process';
import { describe, expect, it, vi } from 'vitest';
import { getAsyncProcessRunnerConfig } from '@push-based/testing-utils';
import { type ProcessObserver, executeProcess } from './execute-process.js';

describe('executeProcess', () => {
  const spyObserver: ProcessObserver = {
    onStdout: vi.fn(),
    onStderr: vi.fn(),
    onError: vi.fn(),
    onComplete: vi.fn(),
  };
  const errorSpy = vi.fn();

  beforeEach(() => {
    vi.clearAllMocks();
  });

  it('should work with node command `node -v`', async () => {
    const processResult = await executeProcess({
      command: `node`,
      args: ['-v'],
      observer: spyObserver,
    });

    // Note: called once or twice depending on environment (2nd time for a new line)
    expect(spyObserver.onStdout).toHaveBeenCalled();
    expect(spyObserver.onComplete).toHaveBeenCalledOnce();
    expect(spyObserver.onError).not.toHaveBeenCalled();
    expect(processResult.stdout).toMatch(/v\d{1,2}(\.\d{1,2}){0,2}/);
  });

  it('should work with npx command `npx --help`', async () => {
    const processResult = await executeProcess({
      command: `npx`,
      args: ['--help'],
      observer: spyObserver,
    });
    expect(spyObserver.onStdout).toHaveBeenCalledOnce();
    expect(spyObserver.onComplete).toHaveBeenCalledOnce();
    expect(spyObserver.onError).not.toHaveBeenCalled();
    expect(processResult.stdout).toContain('npm exec');
  });

  it('should work with script `node custom-script.js`', async () => {
    const processResult = await executeProcess({
      ...getAsyncProcessRunnerConfig({ interval: 10, runs: 4 }),
      observer: spyObserver,
    }).catch(errorSpy);

    expect(errorSpy).not.toHaveBeenCalled();
    expect(processResult.stdout).toContain('process:complete');
    expect(spyObserver.onStdout).toHaveBeenCalledTimes(6); // intro + 4 runs + complete
    expect(spyObserver.onError).not.toHaveBeenCalled();
    expect(spyObserver.onComplete).toHaveBeenCalledOnce();
  });

  it('should work with async script `node custom-script.js` that throws an error', async () => {
    const processResult = await executeProcess({
      ...getAsyncProcessRunnerConfig({
        interval: 10,
        runs: 1,
        throwError: true,
      }),
      observer: spyObserver,
    }).catch(errorSpy);

    expect(errorSpy).toHaveBeenCalledOnce();
    expect(processResult).toBeUndefined();
    expect(spyObserver.onStdout).toHaveBeenCalledTimes(2); // intro + 1 run before error
    expect(spyObserver.onStdout).toHaveBeenLastCalledWith(
      'process:update\n',
      expect.any(ChildProcess),
    );
    expect(spyObserver.onStderr).toHaveBeenCalled();
    expect(spyObserver.onStderr).toHaveBeenCalledWith(
      expect.stringContaining('dummy-error'),
      expect.any(ChildProcess),
    );
    expect(spyObserver.onError).toHaveBeenCalledOnce();
    expect(spyObserver.onComplete).not.toHaveBeenCalled();
  });

  it('should successfully exit process after an error is thrown when ignoreExitCode is set', async () => {
    const processResult = await executeProcess({
      ...getAsyncProcessRunnerConfig({
        interval: 10,
        runs: 1,
        throwError: true,
      }),
      observer: spyObserver,
      ignoreExitCode: true,
    }).catch(errorSpy);

    expect(errorSpy).not.toHaveBeenCalled();
    expect(processResult.code).toBe(1);
    expect(processResult.stdout).toContain('process:update');
    expect(processResult.stderr).toContain('dummy-error');
    expect(spyObserver.onStdout).toHaveBeenCalledTimes(2); // intro + 1 run before error
    expect(spyObserver.onStderr).toHaveBeenCalled();
    expect(spyObserver.onError).not.toHaveBeenCalled();
    expect(spyObserver.onComplete).toHaveBeenCalledOnce();
  });
});

```

--------------------------------------------------------------------------------
/packages/angular-mcp-server/src/lib/tools/ds/component-contract/list/spec/contract-list-utils.spec.ts:
--------------------------------------------------------------------------------

```typescript
/* eslint-disable prefer-const */
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';

import {
  extractComponentNameFromFile,
  formatBytes,
  getTimeAgo,
  formatContractsByComponent,
} from '../utils/contract-list-utils.js';

import type { ContractFileInfo } from '../models/types.js';

function advanceSystemTimeTo(fixed: Date) {
  vi.useFakeTimers();
  vi.setSystemTime(fixed);
}

function restoreSystemTime() {
  vi.useRealTimers();
}

describe('contract-list-utils', () => {
  afterEach(() => {
    restoreSystemTime();
  });

  describe('extractComponentNameFromFile', () => {
    it('extracts simple component names', () => {
      expect(
        extractComponentNameFromFile('foo-20240208T123456.contract.json'),
      ).toBe('foo');
      expect(extractComponentNameFromFile('bar.contract.json')).toBe('bar');
    });

    it('handles multi-part component names', () => {
      const file = 'my-super-button-20240208T123456.contract.json';
      expect(extractComponentNameFromFile(file)).toBe('my');
    });
  });

  describe('formatBytes', () => {
    it('formats bytes into readable units', () => {
      expect(formatBytes(0)).toBe('0 B');
      expect(formatBytes(512)).toBe('512 B');
      expect(formatBytes(2048)).toBe('2 KB');
      expect(formatBytes(1024 * 1024)).toBe('1 MB');
    });
  });

  describe('getTimeAgo', () => {
    beforeEach(() => {
      advanceSystemTimeTo(new Date('2024-02-10T12:00:00Z'));
    });

    it('returns minutes ago for <1h', () => {
      const ts = new Date('2024-02-10T11:45:00Z').toISOString();
      expect(getTimeAgo(ts)).toBe('15m ago');
    });

    it('returns hours ago for <24h', () => {
      const ts = new Date('2024-02-10T08:00:00Z').toISOString();
      expect(getTimeAgo(ts)).toBe('4h ago');
    });

    it('returns days ago for <7d', () => {
      const ts = new Date('2024-02-07T12:00:00Z').toISOString();
      expect(getTimeAgo(ts)).toBe('3d ago');
    });

    it('returns locale date for older timestamps', () => {
      const ts = new Date('2023-12-25T00:00:00Z').toISOString();
      expect(getTimeAgo(ts)).toContain('2023');
    });
  });

  describe('formatContractsByComponent', () => {
    beforeEach(() => {
      advanceSystemTimeTo(new Date('2024-02-10T12:00:00Z'));
    });

    it('groups contracts by component and formats output', () => {
      const contracts: ContractFileInfo[] = [
        {
          fileName: 'foo-20240210T090000.contract.json',
          filePath: '/contracts/foo-20240210T090000.contract.json',
          componentName: 'foo',
          timestamp: new Date('2024-02-10T09:00:00Z').toISOString(),
          hash: 'abcdef1234567890',
          size: '5 KB',
        },
        {
          fileName: 'foo-20240209T090000.contract.json',
          filePath: '/contracts/foo-20240209T090000.contract.json',
          componentName: 'foo',
          timestamp: new Date('2024-02-09T09:00:00Z').toISOString(),
          hash: '123456abcdef7890',
          size: '4 KB',
        },
        {
          fileName: 'bar-20240210T090000.contract.json',
          filePath: '/contracts/bar-20240210T090000.contract.json',
          componentName: 'bar',
          timestamp: new Date('2024-02-10T09:00:00Z').toISOString(),
          hash: 'fedcba9876543210',
          size: '6 KB',
        },
      ];

      const output = formatContractsByComponent(contracts);

      expect(output).toEqual(
        expect.arrayContaining([
          expect.stringMatching(/^🎯 foo:/),
          expect.stringMatching(/^🎯 bar:/),
        ]),
      );

      expect(output).toEqual(
        expect.arrayContaining([
          expect.stringContaining('foo-20240210T090000.contract.json'),
          expect.stringContaining('bar-20240210T090000.contract.json'),
        ]),
      );
    });
  });
});

```

--------------------------------------------------------------------------------
/packages/minimal-repo/packages/application/src/app/components/refactoring-tests/group-3/bad-mixed-3.component.spec.ts:
--------------------------------------------------------------------------------

```typescript
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { MixedStylesComponent3 } from './bad-mixed-3.component';

describe('MixedStylesComponent', () => {
  let component: MixedStylesComponent3;
  let fixture: ComponentFixture<MixedStylesComponent3>;

  beforeEach(async () => {
    await TestBed.configureTestingModule({
      imports: [MixedStylesComponent3],
    }).compileComponents();

    fixture = TestBed.createComponent(MixedStylesComponent3);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('should create', () => {
    expect(component).toBeTruthy();
  });

  it('should render template', () => {
    fixture.detectChanges();
    expect(fixture.nativeElement).toBeTruthy();
  });

  describe('Template Content Tests', () => {
    beforeEach(() => {
      fixture.detectChanges();
    });

    it('should render DS modal component', () => {
      const dsModal = fixture.nativeElement.querySelector('ds-modal');
      expect(dsModal).toBeTruthy();
      expect(dsModal.hasAttribute('open')).toBe(true);
    });

    it('should render ds-modal with proper structure', () => {
      const dsModal = fixture.nativeElement.querySelector('ds-modal');
      expect(dsModal).toBeTruthy();

      const modalContent = dsModal.querySelector('ds-modal-content');
      expect(modalContent).toBeTruthy();
    });

    it('should display modal content text', () => {
      const dsModalContent = fixture.nativeElement.querySelector('ds-modal p');
      expect(dsModalContent?.textContent?.trim()).toBe('Good Modal Content');

      const dsModalHeader =
        fixture.nativeElement.querySelector('ds-modal h2');
      expect(dsModalHeader?.textContent?.trim()).toBe('Good Modal');
    });

    it('should render buttons with different implementations', () => {
      const dsButton = fixture.nativeElement.querySelector('ds-button');
      const legacyButton = fixture.nativeElement.querySelector('button.btn');

      expect(dsButton).toBeTruthy();
      expect(legacyButton).toBeTruthy();
    });

    it('should render progress bars', () => {
      const dsProgressBar =
        fixture.nativeElement.querySelector('ds-progress-bar');
      const legacyProgressBar =
        fixture.nativeElement.querySelector('div.progress-bar');

      expect(dsProgressBar).toBeTruthy();
      expect(legacyProgressBar).toBeTruthy();
    });

    it('should render dropdown components', () => {
      const dsDropdown = fixture.nativeElement.querySelector('ds-dropdown');
      const legacyDropdown =
        fixture.nativeElement.querySelector('select.dropdown');

      expect(dsDropdown).toBeTruthy();
      expect(legacyDropdown).toBeTruthy();
    });

    it('should render alert components', () => {
      const dsAlert = fixture.nativeElement.querySelector('ds-alert');
      const legacyAlert = fixture.nativeElement.querySelector('div.alert');

      expect(dsAlert).toBeTruthy();
      expect(legacyAlert).toBeTruthy();
    });

    it('should render tooltip components', () => {
      const dsTooltip = fixture.nativeElement.querySelector('ds-tooltip');
      const legacyTooltip = fixture.nativeElement.querySelector('div.tooltip');

      expect(dsTooltip).toBeTruthy();
      expect(legacyTooltip).toBeTruthy();
    });

    it('should render breadcrumb navigation', () => {
      const dsBreadcrumb = fixture.nativeElement.querySelector('ds-breadcrumb');
      const legacyBreadcrumb =
        fixture.nativeElement.querySelector('nav.breadcrumb');

      expect(dsBreadcrumb).toBeTruthy();
      expect(legacyBreadcrumb).toBeTruthy();
    });

    it('should have breadcrumb items', () => {
      const breadcrumbItems =
        fixture.nativeElement.querySelectorAll('ds-breadcrumb-item');
      expect(breadcrumbItems.length).toBe(3);
    });
  });
});

```

--------------------------------------------------------------------------------
/packages/angular-mcp-server/src/lib/tools/ds/component-contract/builder/utils/build-contract.ts:
--------------------------------------------------------------------------------

```typescript
import { existsSync, readFileSync } from 'node:fs';
import { createHash } from 'node:crypto';
import { parseComponents } from '@push-based/angular-ast-utils';
import { resolveCrossPlatformPathAndValidate } from '../../../shared/index.js';
import { generateMeta } from './meta.generator.js';
import { extractPublicApi } from './public-api.extractor.js';
import { extractSlotsAndDom } from './dom-slots.extractor.js';
import { collectStylesV2 } from './styles.collector.js';
import { collectInlineStyles } from './inline-styles.collector.js';
import type { ComponentContract } from '../../shared/models/types.js';
import { relative } from 'node:path';

/**
 * Build a complete component contract from template and style files.
 * Template and style paths can be the same as TypeScript path for inline templates/styles.
 */
export async function buildComponentContract(
  templatePath: string,
  scssPath: string,
  cwd: string,
  typescriptPath: string,
): Promise<ComponentContract> {
  const componentTsPath = resolveCrossPlatformPathAndValidate(
    cwd,
    typescriptPath,
  );

  // Validate TypeScript file exists (required)
  if (!existsSync(componentTsPath)) {
    throw new Error(`Component TypeScript file not found: ${componentTsPath}`);
  }

  // Resolve and validate template path
  // If it's the same as TS path, it means inline template
  const resolvedTemplatePath = resolveCrossPlatformPathAndValidate(
    cwd,
    templatePath,
  );
  const isInlineTemplate = resolvedTemplatePath === componentTsPath;

  if (!isInlineTemplate && !existsSync(resolvedTemplatePath)) {
    throw new Error(`Template file not found: ${resolvedTemplatePath}`);
  }

  // Resolve and validate style path
  // If it's the same as TS path, it means inline styles or no external styles
  const resolvedScssPath = resolveCrossPlatformPathAndValidate(cwd, scssPath);
  const isInlineOrNoStyles = resolvedScssPath === componentTsPath;

  if (!isInlineOrNoStyles && !existsSync(resolvedScssPath)) {
    throw new Error(`Style file not found: ${resolvedScssPath}`);
  }

  const sources = {
    ts: readFileSync(componentTsPath, 'utf-8'),
    scss: isInlineOrNoStyles ? '' : readFileSync(resolvedScssPath, 'utf-8'),
    template: isInlineTemplate
      ? ''
      : readFileSync(resolvedTemplatePath, 'utf-8'),
  };

  const [parsedComponent] = await parseComponents([componentTsPath]);
  if (!parsedComponent) {
    throw new Error(`Failed to parse component: ${componentTsPath}`);
  }

  const relativeTemplatePath = relative(cwd, resolvedTemplatePath);
  const relativeScssPath = relative(cwd, resolvedScssPath);

  const meta = generateMeta(
    relativeTemplatePath,
    parsedComponent,
    isInlineTemplate,
  );
  const publicApi = extractPublicApi(parsedComponent);
  const { slots, dom } = await extractSlotsAndDom(parsedComponent);

  const styleBuckets: import('../../shared/models/types.js').StyleDeclarations[] =
    [];

  if (!isInlineOrNoStyles) {
    const externalStyles = await collectStylesV2(resolvedScssPath, dom);
    externalStyles.sourceFile = relativeScssPath;
    styleBuckets.push(externalStyles);
  }

  const inlineStyles = await collectInlineStyles(parsedComponent, dom);
  styleBuckets.push(inlineStyles);

  const styles = styleBuckets.reduce<
    import('../../shared/models/types.js').StyleDeclarations
  >(
    (acc, bucket) => {
      acc.rules = { ...acc.rules, ...bucket.rules };
      return acc;
    },
    {
      sourceFile:
        styleBuckets.length > 0
          ? styleBuckets[styleBuckets.length - 1].sourceFile
          : relativeScssPath,
      rules: {},
    },
  );

  const hash = createHash('sha256')
    .update(sources.template + sources.scss + sources.ts)
    .digest('hex');

  return {
    meta: { ...meta, hash },
    publicApi,
    slots,
    dom,
    styles,
  };
}

```

--------------------------------------------------------------------------------
/testing/utils/src/lib/os-agnostic-paths.unit.test.ts:
--------------------------------------------------------------------------------

```typescript
import {
  type MockInstance,
  afterEach,
  beforeEach,
  describe,
  expect,
  it,
  vi,
} from 'vitest';
import { osAgnosticPath } from './os-agnostic-paths';

describe('osAgnosticPath', () => {
  const cwdSpy: MockInstance<[], string> = vi.spyOn(process, 'cwd');

  it('should forward nullish paths on Linux/macOS and Windows', () => {
    expect(osAgnosticPath(undefined)).toBeUndefined();
  });

  describe('Unix-based systems (Linux/macOS)', () => {
    const unixCwd = '/Users/jerry';

    beforeEach(() => {
      cwdSpy.mockReturnValue(unixCwd);
    });

    afterEach(() => {
      cwdSpy.mockReset();
    });

    it('should convert a path within the CWD to an OS-agnostic path on Linux/macOS', () => {
      expect(
        osAgnosticPath(`${unixCwd}/.code-pushup/.code-pushup.config.ts`),
      ).toBe('<CWD>/.code-pushup/.code-pushup.config.ts');
    });

    it('should return paths outside of CWD on Linux/macOS', () => {
      expect(
        osAgnosticPath(`${unixCwd}/../.code-pushup/.code-pushup.config.ts`),
      ).toBe('../.code-pushup/.code-pushup.config.ts');
    });

    it('should handle absolute paths correctly on Linux/macOS', () => {
      expect(osAgnosticPath('/.code-pushup/.code-pushup.config.ts')).toBe(
        '/.code-pushup/.code-pushup.config.ts',
      );
    });

    it('should handle paths with CWD shorthand "." correctly on Linux/macOS', () => {
      expect(osAgnosticPath('./.code-pushup/.code-pushup.config.ts')).toBe(
        './.code-pushup/.code-pushup.config.ts',
      );
    });

    it('should handle relative paths correctly on Linux/macOS', () => {
      expect(osAgnosticPath('../../.code-pushup/.code-pushup.config.ts')).toBe(
        '../../.code-pushup/.code-pushup.config.ts',
      );
    });

    it('should handle path segments correctly on Linux/macOS', () => {
      expect(osAgnosticPath('.code-pushup/.code-pushup.config.ts')).toBe(
        '.code-pushup/.code-pushup.config.ts',
      );
    });

    it('should NOT modify already OS-agnostic paths on Linux/macOS', () => {
      expect(osAgnosticPath('<CWD>/.code-pushup/.code-pushup.config.ts')).toBe(
        '<CWD>/.code-pushup/.code-pushup.config.ts',
      );
    });
  });

  describe('Windows', () => {
    const windowsCWD = String.raw`D:\\users\\jerry`;

    beforeEach(() => {
      cwdSpy.mockReturnValue(windowsCWD);
    });

    afterEach(() => {
      cwdSpy.mockReset();
    });

    it('should return paths outside of CWD on Windows', () => {
      expect(
        osAgnosticPath(
          `${windowsCWD}\\..\\.code-pushup\\.code-pushup.config.ts`,
        ),
      ).toBe('../.code-pushup/.code-pushup.config.ts');
    });

    it('should convert a path within the CWD to an OS-agnostic path on Windows', () => {
      expect(
        osAgnosticPath(`${windowsCWD}\\.code-pushup\\.code-pushup.config.ts`),
      ).toBe('<CWD>/.code-pushup/.code-pushup.config.ts');
    });

    it('should handle absolute paths correctly on Windows', () => {
      expect(
        osAgnosticPath(String.raw`\.code-pushup\.code-pushup.config.ts`),
      ).toBe('/.code-pushup/.code-pushup.config.ts');
    });

    it('should handle paths with CWD shorthand "." correctly on Windows', () => {
      expect(
        osAgnosticPath(String.raw`.\.code-pushup\.code-pushup.config.ts`),
      ).toBe('./.code-pushup/.code-pushup.config.ts');
    });

    it('should handle relative paths correctly on Windows', () => {
      expect(
        osAgnosticPath(String.raw`..\..\.code-pushup\.code-pushup.config.ts`),
      ).toBe('../../.code-pushup/.code-pushup.config.ts');
    });

    it('should handle path segments correctly on Windows', () => {
      expect(
        osAgnosticPath(String.raw`.code-pushup\.code-pushup.config.ts`),
      ).toBe('.code-pushup/.code-pushup.config.ts');
    });
  });
});

```

--------------------------------------------------------------------------------
/packages/angular-mcp-server/src/lib/tools/ds/component-contract/builder/spec/styles.collector.spec.ts:
--------------------------------------------------------------------------------

```typescript
/* eslint-disable prefer-const */
import { describe, it, expect, vi, beforeEach } from 'vitest';

// -----------------------------------------------------------------------------
// Mocks for dependencies used by styles.collector.ts
// -----------------------------------------------------------------------------

// fs.readFileSync mock
let readFileSyncMock: any;
vi.mock('node:fs', () => ({
  get readFileSync() {
    return readFileSyncMock;
  },
}));

// Initialize after mock registration
readFileSyncMock = vi.fn();

// style AST utilities mocks
let parseStylesheetMock: any;
let visitEachChildMock: any;
vi.mock('@push-based/styles-ast-utils', () => ({
  get parseStylesheet() {
    return parseStylesheetMock;
  },
  get visitEachChild() {
    return visitEachChildMock;
  },
}));

// Initialize after mock registration
parseStylesheetMock = vi.fn();
visitEachChildMock = vi.fn();

// selectorMatches mock
let selectorMatchesMock: any;
vi.mock('../utils/css-match.js', () => ({
  get selectorMatches() {
    return selectorMatchesMock;
  },
}));

// Initialize after mock registration
selectorMatchesMock = vi.fn();

// SUT
import { collectStylesV2 } from '../utils/styles.collector.js';

// -----------------------------------------------------------------------------
// Helper to fabricate Rule objects understood by collectStylesV2
// -----------------------------------------------------------------------------
function createRule(selector: string, declarations: Record<string, string>) {
  return {
    selector,
    walkDecls(callback: (decl: { prop: string; value: string }) => void) {
      Object.entries(declarations).forEach(([prop, value]) =>
        callback({ prop, value }),
      );
    },
  } as any;
}

function resetMocks() {
  readFileSyncMock.mockReset();
  parseStylesheetMock.mockReset();
  visitEachChildMock.mockReset();
  selectorMatchesMock.mockReset();
}

// -----------------------------------------------------------------------------
// Tests
// -----------------------------------------------------------------------------

describe('collectStylesV2', () => {
  const scssPath = '/styles.scss';

  beforeEach(() => {
    resetMocks();
    readFileSyncMock.mockReturnValue('dummy');
    // parseStylesheet returns root obj with type root
    parseStylesheetMock.mockReturnValue({ root: { type: 'root' } });
  });

  it('collects properties and matches dom elements', async () => {
    const dom = {
      div: {} as any,
    };

    // Provide one rule 'div { color:red }'
    visitEachChildMock.mockImplementation((_root: any, visitor: any) => {
      visitor.visitRule(createRule('div', { color: 'red' }));
    });

    selectorMatchesMock.mockImplementation(
      (css: string, domKey: string) => css === 'div' && domKey === 'div',
    );

    const styles = await collectStylesV2(scssPath, dom as any);

    expect(styles.sourceFile).toBe(scssPath);
    expect(styles.rules.div).toBeDefined();
    expect(styles.rules.div.properties).toEqual({ color: 'red' });
    expect(styles.rules.div.appliesTo).toEqual(['div']);
  });

  it('handles multiple rules and appliesTo filtering', async () => {
    const dom = {
      div: {} as any,
      'span.foo': {} as any,
    };

    visitEachChildMock.mockImplementation((_root: any, visitor: any) => {
      visitor.visitRule(createRule('div', { margin: '0' }));
      visitor.visitRule(createRule('.foo', { padding: '1rem' }));
    });

    selectorMatchesMock.mockImplementation((css: string, domKey: string) => {
      if (css === 'div') return domKey === 'div';
      if (css === '.foo') return domKey === 'span.foo';
      return false;
    });

    const styles = await collectStylesV2(scssPath, dom as any);

    expect(styles.rules.div.appliesTo).toEqual(['div']);
    expect(styles.rules['.foo'].appliesTo).toEqual(['span.foo']);
  });
});

```
Page 3/7FirstPrevNextLast