#
tokens: 49767/50000 59/473 files (page 3/10)
lines: on (toggle) GitHub
raw markdown copy reset
This is page 3 of 10. Use http://codebase.md/push-based/angular-toolkit-mcp?lines=true&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/minimal-repo/packages/application/src/app/styles/new-styles-10.scss:
--------------------------------------------------------------------------------

```scss
  1 | // Example SCSS file with deprecated and non-deprecated classes
  2 | .example-class-19 {
  3 |   border-bottom: 1px solid #ddd;
  4 | }
  5 | 
  6 | .form-control-tabs-segmented-v3 {
  7 |   padding: 5px;
  8 | }
  9 | 
 10 | .example-class-20 {
 11 |   margin-left: 20px;
 12 | }
 13 | 
 14 | .form-control-tabs-segmented-v4 {
 15 |   border-radius: 3px;
 16 | }
 17 | 
 18 | .example-class-21 {
 19 |   color: red;
 20 | }
 21 | 
 22 | .example-class-22 {
 23 |   background-color: green;
 24 | }
 25 | 
 26 | .example-class-23 {
 27 |   font-size: 18px;
 28 | }
 29 | 
 30 | .example-class-24 {
 31 |   padding: 10px;
 32 | }
 33 | 
 34 | .example-class-25 {
 35 |   margin: 5px;
 36 | }
 37 | 
 38 | .example-class-26 {
 39 |   border: 1px solid black;
 40 | }
 41 | 
 42 | .example-class-27 {
 43 |   text-align: left;
 44 | }
 45 | 
 46 | .example-class-28 {
 47 |   line-height: 2;
 48 | }
 49 | 
 50 | .example-class-29 {
 51 |   font-weight: normal;
 52 | }
 53 | 
 54 | .example-class-30 {
 55 |   display: inline-block;
 56 | }
 57 | 
 58 | .example-class-31 {
 59 |   width: 50%;
 60 | }
 61 | 
 62 | .example-class-32 {
 63 |   height: 100px;
 64 | }
 65 | 
 66 | .example-class-33 {
 67 |   overflow: auto;
 68 | }
 69 | 
 70 | .example-class-34 {
 71 |   position: relative;
 72 | }
 73 | 
 74 | .example-class-35 {
 75 |   top: 10px;
 76 | }
 77 | 
 78 | .example-class-36 {
 79 |   left: 20px;
 80 | }
 81 | 
 82 | .example-class-37 {
 83 |   right: 30px;
 84 | }
 85 | 
 86 | .example-class-38 {
 87 |   bottom: 40px;
 88 | }
 89 | 
 90 | .example-class-39 {
 91 |   z-index: 1;
 92 | }
 93 | 
 94 | .example-class-40 {
 95 |   opacity: 0.5;
 96 | }
 97 | 
 98 | .example-class-41 {
 99 |   visibility: hidden;
100 | }
101 | 
102 | .example-class-42 {
103 |   cursor: pointer;
104 | }
105 | 
106 | .example-class-43 {
107 |   transition: all 0.3s ease;
108 | }
109 | 
110 | .example-class-44 {
111 |   transform: rotate(45deg);
112 | }
113 | 
114 | .example-class-45 {
115 |   animation: fadeIn 1s;
116 | }
117 | 
118 | .example-class-46 {
119 |   box-sizing: border-box;
120 | }
121 | 
122 | .example-class-47 {
123 |   content: '';
124 | }
125 | 
126 | .example-class-48 {
127 |   clip: rect(0, 0, 0, 0);
128 | }
129 | 
130 | .example-class-49 {
131 |   float: left;
132 | }
133 | 
134 | .example-class-50 {
135 |   clear: both;
136 | }
137 | 
```

--------------------------------------------------------------------------------
/packages/minimal-repo/packages/application/src/app/styles/new-styles-9.scss:
--------------------------------------------------------------------------------

```scss
  1 | // Example SCSS file with deprecated and non-deprecated classes
  2 | .example-class-17 {
  3 |   padding-top: 10px;
  4 | }
  5 | 
  6 | .form-control-tabs-segmented-flex {
  7 |   flex-direction: row;
  8 | }
  9 | 
 10 | .example-class-18 {
 11 |   font-size: 16px;
 12 | }
 13 | 
 14 | .form-control-tabs-segmented-v2-dark {
 15 |   background-color: #333;
 16 | }
 17 | 
 18 | .example-class-21 {
 19 |   color: red;
 20 | }
 21 | 
 22 | .example-class-22 {
 23 |   background-color: green;
 24 | }
 25 | 
 26 | .example-class-23 {
 27 |   font-size: 18px;
 28 | }
 29 | 
 30 | .example-class-24 {
 31 |   padding: 10px;
 32 | }
 33 | 
 34 | .example-class-25 {
 35 |   margin: 5px;
 36 | }
 37 | 
 38 | .example-class-26 {
 39 |   border: 1px solid black;
 40 | }
 41 | 
 42 | .example-class-27 {
 43 |   text-align: left;
 44 | }
 45 | 
 46 | .example-class-28 {
 47 |   line-height: 2;
 48 | }
 49 | 
 50 | .example-class-29 {
 51 |   font-weight: normal;
 52 | }
 53 | 
 54 | .example-class-30 {
 55 |   display: inline-block;
 56 | }
 57 | 
 58 | .example-class-31 {
 59 |   width: 50%;
 60 | }
 61 | 
 62 | .example-class-32 {
 63 |   height: 100px;
 64 | }
 65 | 
 66 | .example-class-33 {
 67 |   overflow: auto;
 68 | }
 69 | 
 70 | .example-class-34 {
 71 |   position: relative;
 72 | }
 73 | 
 74 | .example-class-35 {
 75 |   top: 10px;
 76 | }
 77 | 
 78 | .example-class-36 {
 79 |   left: 20px;
 80 | }
 81 | 
 82 | .example-class-37 {
 83 |   right: 30px;
 84 | }
 85 | 
 86 | .example-class-38 {
 87 |   bottom: 40px;
 88 | }
 89 | 
 90 | .example-class-39 {
 91 |   z-index: 1;
 92 | }
 93 | 
 94 | .example-class-40 {
 95 |   opacity: 0.5;
 96 | }
 97 | 
 98 | .example-class-41 {
 99 |   visibility: hidden;
100 | }
101 | 
102 | .example-class-42 {
103 |   cursor: pointer;
104 | }
105 | 
106 | .example-class-43 {
107 |   transition: all 0.3s ease;
108 | }
109 | 
110 | .example-class-44 {
111 |   transform: rotate(45deg);
112 | }
113 | 
114 | .example-class-45 {
115 |   animation: fadeIn 1s;
116 | }
117 | 
118 | .example-class-46 {
119 |   box-sizing: border-box;
120 | }
121 | 
122 | .example-class-47 {
123 |   content: '';
124 | }
125 | 
126 | .example-class-48 {
127 |   clip: rect(0, 0, 0, 0);
128 | }
129 | 
130 | .example-class-49 {
131 |   float: left;
132 | }
133 | 
134 | .example-class-50 {
135 |   clear: both;
136 | }
137 | 
```

--------------------------------------------------------------------------------
/packages/shared/ds-component-coverage/src/lib/runner/audits/ds-coverage/utils.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { Audit, AuditOutput, Issue } from '@code-pushup/models';
 2 | import { pluralize, slugify } from '@code-pushup/utils';
 3 | import { ComponentReplacement } from './schema.js';
 4 | 
 5 | /**
 6 |  * Creates a scored audit output.
 7 |  * @returns Audit output.
 8 |  * @param componentReplacements
 9 |  */
10 | export function getCompUsageAudits(
11 |   componentReplacements: ComponentReplacement[],
12 | ): Audit[] {
13 |   return componentReplacements.map((comp) => ({
14 |     slug: getCompCoverageAuditSlug(comp),
15 |     title: getCompCoverageAuditTitle(comp),
16 |     description: getCompCoverageAuditDescription(comp),
17 |   }));
18 | }
19 | 
20 | /**
21 |  * Creates a scored audit output.
22 |  * @param componentName
23 |  * @param issues
24 |  * @returns Audit output.
25 |  */
26 | export function getCompCoverageAuditOutput(
27 |   componentName: ComponentReplacement,
28 |   issues: Issue[],
29 | ): AuditOutput {
30 |   return {
31 |     slug: getCompCoverageAuditSlug(componentName),
32 |     displayValue: `${issues.length} ${pluralize('class', issues.length)} found`,
33 |     score: issues.length === 0 ? 1 : 0,
34 |     value: issues.length,
35 |     details: {
36 |       issues,
37 |     },
38 |   };
39 | }
40 | 
41 | export function getCompCoverageAuditSlug({
42 |   componentName,
43 | }: ComponentReplacement): string {
44 |   return slugify(`coverage-${componentName}`);
45 | }
46 | export function getCompCoverageAuditTitle({
47 |   componentName,
48 | }: ComponentReplacement): string {
49 |   return `Usage coverage for ${componentName} component`;
50 | }
51 | export function getCompCoverageAuditDescription({
52 |   componentName,
53 |   deprecatedCssClasses,
54 | }: ComponentReplacement): string {
55 |   return `Coverage audit for ${componentName} component. Matching classes: ${deprecatedCssClasses.join(
56 |     ', ',
57 |   )}`;
58 | }
59 | 
```

--------------------------------------------------------------------------------
/packages/shared/utils/src/lib/file/file.resolver.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { existsSync } from 'fs';
 2 | import { readFile } from 'fs/promises';
 3 | import * as path from 'path';
 4 | 
 5 | export const fileResolverCache = new Map<string, Promise<string>>();
 6 | 
 7 | /**
 8 |  * Resolves a file content from the file system, caching the result
 9 |  * to avoid reading the same file multiple times.
10 |  *
11 |  * This function returns a Promise that resolves to the file content.
12 |  * This is important to avoid reading the same file multiple times.
13 |  * @param filePath
14 |  */
15 | export async function resolveFileCached(filePath: string): Promise<string> {
16 |   const normalizedPath = path.normalize(filePath);
17 |   if (!existsSync(normalizedPath)) {
18 |     throw new Error(`File not found: ${normalizedPath}`);
19 |   }
20 | 
21 |   if (fileResolverCache.has(normalizedPath)) {
22 |     const cachedPromise = fileResolverCache.get(normalizedPath);
23 |     if (cachedPromise) {
24 |       return cachedPromise;
25 |     }
26 |   }
27 | 
28 |   const fileReadOperationPromise = resolveFile(filePath)
29 |     .then((content) => {
30 |       fileResolverCache.set(normalizedPath, Promise.resolve(content));
31 |       return content;
32 |     })
33 |     .catch((ctx) => {
34 |       fileResolverCache.delete(normalizedPath);
35 |       throw ctx;
36 |     });
37 | 
38 |   fileResolverCache.set(normalizedPath, fileReadOperationPromise);
39 |   return fileReadOperationPromise;
40 | }
41 | 
42 | /**
43 |  * Resolves a file content from the file system directly, bypassing any cache.
44 |  *
45 |  * @param filePath
46 |  */
47 | export async function resolveFile(filePath: string): Promise<string> {
48 |   const normalizedPath = path.normalize(filePath);
49 |   if (!existsSync(normalizedPath)) {
50 |     throw new Error(`File not found: ${normalizedPath}`);
51 |   }
52 |   return readFile(normalizedPath, 'utf-8');
53 | }
54 | 
```

--------------------------------------------------------------------------------
/tools/nx-advanced-profile.js:
--------------------------------------------------------------------------------

```javascript
 1 | import { fork } from 'node:child_process';
 2 | import { fileURLToPath } from 'node:url';
 3 | 
 4 | export async function nxRunWithPerfLogging(
 5 |   args,
 6 |   {
 7 |     verbose = false,
 8 |     noPatch = false,
 9 |     onData = () => {},
10 |     onTraceEvent = () => {},
11 |     onMetadata = () => {},
12 |     beforeExit = () => {},
13 |   } = {},
14 | ) {
15 |   const patch = !noPatch;
16 |   const nxUrl = await import.meta.resolve('nx');
17 |   const nxPath = fileURLToPath(nxUrl);
18 | 
19 |   const profile = {
20 |     metadata: {},
21 |     traceEvents: [],
22 |   };
23 | 
24 |   const forkArgs = [
25 |     nxPath,
26 |     args,
27 |     {
28 |       stdio: ['pipe', 'pipe', 'pipe', 'ipc'],
29 |       env: {
30 |         ...process.env,
31 |         NX_DAEMON: 'false',
32 |         NX_CACHE: 'false',
33 |         NX_PERF_LOGGING: 'true',
34 |       },
35 |       // Preload the patch file so that it applies before NX is loaded.
36 |       execArgv: patch ? ['--require', './tools/perf_hooks.patch.js'] : [],
37 |     },
38 |   ];
39 |   if (verbose) {
40 |     console.log('Forking NX with args:', forkArgs);
41 |   }
42 | 
43 |   const child = fork(...forkArgs);
44 | 
45 |   child.stdout?.on('data', (data) => {
46 |     const lines = data.toString().split('\n');
47 |     for (const line of lines) {
48 |       onData(line);
49 |       const res = line.split(':JSON:');
50 | 
51 |       if (res.length === 2) {
52 |         const [prop, jsonString] = res;
53 |         const perfProfileEvent = JSON.parse(jsonString?.trim() || '{}');
54 |         if (prop === 'traceEvent') {
55 |           onTraceEvent(perfProfileEvent);
56 |           profile.traceEvents.push(perfProfileEvent);
57 |         }
58 |         if (prop === 'metadata') {
59 |           onMetadata(perfProfileEvent);
60 |           profile.metadata = perfProfileEvent;
61 |         }
62 |       }
63 |     }
64 |   });
65 | 
66 |   child.on('close', () => {
67 |     beforeExit(profile);
68 |   });
69 | }
70 | 
```

--------------------------------------------------------------------------------
/packages/shared/utils/src/lib/file/default-export-loader.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { pathToFileURL } from 'node:url';
 2 | 
 3 | /**
 4 |  * Dynamically imports an ES Module and extracts the default export.
 5 |  *
 6 |  * @param filePath - Absolute path to the ES module file to import
 7 |  * @returns The default export from the module
 8 |  * @throws Error if the module cannot be loaded or has no default export
 9 |  *
10 |  * @example
11 |  * ```typescript
12 |  * const data = await loadDefaultExport('/path/to/config.js');
13 |  * ```
14 |  */
15 | export async function loadDefaultExport<T = unknown>(
16 |   filePath: string,
17 | ): Promise<T> {
18 |   try {
19 |     const fileUrl = pathToFileURL(filePath).toString();
20 | 
21 |     // In test environments (Vitest), use native import to avoid transformation issues
22 |     // In production (webpack/bundled), use Function constructor to preserve dynamic import
23 |     const isTestEnv =
24 |       typeof process !== 'undefined' &&
25 |       (process.env.NODE_ENV === 'test' ||
26 |         process.env.VITEST === 'true' ||
27 |         typeof (globalThis as Record<string, unknown>).vitest !== 'undefined');
28 | 
29 |     const module = isTestEnv
30 |       ? await import(fileUrl)
31 |       : await new Function('url', 'return import(url)')(fileUrl);
32 | 
33 |     if (!('default' in module)) {
34 |       throw new Error(
35 |         `No default export found in module. Expected ES Module format:\n` +
36 |           `export default [...]\n\n` +
37 |           `Available exports: ${Object.keys(module).join(', ') || 'none'}`,
38 |       );
39 |     }
40 | 
41 |     return module.default;
42 |   } catch (ctx) {
43 |     if (
44 |       ctx instanceof Error &&
45 |       ctx.message.includes('No default export found')
46 |     ) {
47 |       throw ctx;
48 |     }
49 |     throw new Error(
50 |       `Failed to load module from ${filePath}: ${ctx instanceof Error ? ctx.message : String(ctx)}`,
51 |     );
52 |   }
53 | }
54 | 
```

--------------------------------------------------------------------------------
/packages/angular-mcp-server/src/lib/tools/ds/shared/violation-analysis/base-analyzer.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import * as process from 'node:process';
 2 | import { getDeprecatedCssClasses } from '../../component/utils/deprecated-css-helpers.js';
 3 | import { validateComponentName } from '../utils/component-validation.js';
 4 | import {
 5 |   BaseViolationOptions,
 6 |   BaseViolationResult,
 7 |   ReportCoverageParams,
 8 | } from './types.js';
 9 | import { analyzeProjectCoverage as collectFilesViolations } from './coverage-analyzer.js';
10 | 
11 | /**
12 |  * Base analyzer for design system violations - shared logic between file and folder reporting
13 |  */
14 | export async function analyzeViolationsBase<T extends BaseViolationResult>(
15 |   options: BaseViolationOptions,
16 | ): Promise<T> {
17 |   const {
18 |     cwd = process.cwd(),
19 |     directory,
20 |     componentName,
21 |     deprecatedCssClassesPath,
22 |   } = options;
23 | 
24 |   validateComponentName(componentName);
25 | 
26 |   if (!directory || typeof directory !== 'string') {
27 |     throw new Error('Directory parameter is required and must be a string');
28 |   }
29 | 
30 |   process.chdir(cwd);
31 | 
32 |   if (!deprecatedCssClassesPath) {
33 |     throw new Error(
34 |       'Missing ds.deprecatedCssClassesPath. Provide --ds.deprecatedCssClassesPath in mcp.json file.',
35 |     );
36 |   }
37 | 
38 |   const deprecatedCssClasses = await getDeprecatedCssClasses(
39 |     componentName,
40 |     deprecatedCssClassesPath,
41 |     cwd,
42 |   );
43 | 
44 |   const dsComponents = [
45 |     {
46 |       componentName,
47 |       deprecatedCssClasses,
48 |     },
49 |   ];
50 | 
51 |   const params: ReportCoverageParams = {
52 |     cwd,
53 |     returnRawData: true,
54 |     directory,
55 |     dsComponents,
56 |   };
57 | 
58 |   const result = await collectFilesViolations(params);
59 | 
60 |   if (!result.rawData?.rawPluginResult) {
61 |     throw new Error('Failed to get raw plugin result for violation analysis');
62 |   }
63 | 
64 |   return result.rawData.rawPluginResult as T;
65 | }
66 | 
```

--------------------------------------------------------------------------------
/packages/minimal-repo/packages/design-system/component-options.mjs:
--------------------------------------------------------------------------------

```
 1 | const dsComponents = [
 2 |   {
 3 |     componentName: 'DsPill',
 4 |     deprecatedCssClasses: [
 5 |       'pill-with-badge',
 6 |       'pill-with-badge-v2',
 7 |       'sports-pill',
 8 |     ],
 9 |   },
10 |   {
11 |     componentName: 'DsBadge',
12 |     deprecatedCssClasses: ['offer-badge'],
13 |   },
14 |   {
15 |     componentName: 'DsTabsModule',
16 |     deprecatedCssClasses: ['tab-nav', 'nav-tabs', 'tab-nav-item'],
17 |   },
18 |   {
19 |     componentName: 'DsButton',
20 |     deprecatedCssClasses: ['btn', 'btn-primary', 'legacy-button'],
21 |   },
22 |   {
23 |     componentName: 'DsModal',
24 |     deprecatedCssClasses: ['modal'],
25 |   },
26 |   {
27 |     componentName: 'DsCard',
28 |     deprecatedCssClasses: ['card'],
29 |   },
30 |   {
31 |     componentName: 'DsLoadingSpinner',
32 |     deprecatedCssClasses: ['loading', 'loading-v2', 'loading-v3'],
33 |   },
34 |   {
35 |     componentName: 'DsCardExpandable',
36 |     deprecatedCssClasses: ['collapsible-container'],
37 |   },
38 |   {
39 |     componentName: 'DsDivider',
40 |     deprecatedCssClasses: ['divider'],
41 |   },
42 |   {
43 |     componentName: 'DsNotificationBubble',
44 |     deprecatedCssClasses: ['count', 'badge-circle'],
45 |   },
46 |   {
47 |     componentName: 'DsCheckbox',
48 |     deprecatedCssClasses: ['custom-control-checkbox'],
49 |   },
50 |   {
51 |     componentName: 'DsRadioModule',
52 |     deprecatedCssClasses: ['custom-control-radio'],
53 |   },
54 |   {
55 |     componentName: 'DsSegmentedControlModule',
56 |     deprecatedCssClasses: [
57 |       'form-control-tabs-segmented-v2',
58 |       'form-control-tabs-segmented-flex',
59 |       'form-control-tabs-segmented-v2-dark',
60 |       'form-control-tabs-segmented-v3',
61 |       'form-control-tabs-segmented-v4',
62 |       'form-control-tabs-segmented',
63 |     ],
64 |   },
65 |   {
66 |     componentName: 'DsSwitch',
67 |     deprecatedCssClasses: ['custom-control-switcher'],
68 |   },
69 | ];
70 | 
71 | export default dsComponents;
72 | 
```

--------------------------------------------------------------------------------
/testing/vitest-setup/src/lib/fs-memfs.setup-file.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import {
 2 |   MockInstance,
 3 |   afterEach,
 4 |   beforeEach,
 5 |   vi,
 6 |   beforeAll,
 7 |   afterAll,
 8 | } from 'vitest';
 9 | 
10 | // Define the constant locally since cross-project imports cause build issues
11 | const MEMFS_VOLUME = '/memfs';
12 | 
13 | /**
14 |  * Mocks the fs and fs/promises modules with memfs.
15 |  */
16 | 
17 | type Memfs = typeof import('memfs');
18 | 
19 | vi.mock('fs', async () => {
20 |   const memfs: Memfs = await vi.importActual('memfs');
21 |   return memfs.fs;
22 | });
23 | 
24 | vi.mock('fs/promises', async () => {
25 |   const memfs: Memfs = await vi.importActual('memfs');
26 |   return memfs.fs.promises;
27 | });
28 | 
29 | /**
30 |  * Mocks the current working directory to MEMFS_VOLUME.
31 |  * This is useful when you use relative paths in your code
32 |  * @type {MockInstance<[], string>}
33 |  *
34 |  * @example
35 |  * - `readFile('./file.txt')` reads MEMFS_VOLUME/file.txt
36 |  * - `readFile(join(process.cwd(), 'file.txt'))` reads MEMFS_VOLUME/file.txt
37 |  * - `readFile('file.txt')` reads file.txt
38 |  */
39 | let cwdSpy: MockInstance;
40 | 
41 | // This covers arrange blocks at the top of a "describe" block
42 | beforeAll(() => {
43 |   cwdSpy = vi.spyOn(process, 'cwd').mockReturnValue(MEMFS_VOLUME);
44 | });
45 | 
46 | // Clear mock usage data in arrange blocks as well as usage of the API in each "it" block.
47 | // docs: https://vitest.dev/api/mock.html#mockclear
48 | beforeEach(() => {
49 |   cwdSpy.mockClear();
50 | });
51 | 
52 | // Restore mock implementation and usage data "it" block
53 | // Mock implementations remain if given. => vi.fn(impl).mockRestore() === vi.fn(impl)
54 | // docs: https://vitest.dev/api/mock.html#mockrestore
55 | afterEach(() => {
56 |   cwdSpy.mockRestore();
57 | });
58 | 
59 | // Restore the original implementation after all "describe" block in a file
60 | // docs: https://vitest.dev/api/mock.html#mockreset
61 | afterAll(() => {
62 |   cwdSpy.mockReset();
63 | });
64 | 
```

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

```typescript
 1 | import { ToolSchemaOptions } from '@push-based/models';
 2 | import { COMMON_ANNOTATIONS } from '../../../shared/index.js';
 3 | 
 4 | /**
 5 |  * Schema for building component contracts
 6 |  */
 7 | export const buildComponentContractSchema: ToolSchemaOptions = {
 8 |   name: 'build_component_contract',
 9 |   description:
10 |     "Generate a static surface contract for a component's template and SCSS.",
11 |   inputSchema: {
12 |     type: 'object',
13 |     properties: {
14 |       saveLocation: {
15 |         type: 'string',
16 |         description:
17 |           'Path where to save the contract file. Supports both absolute and relative paths.',
18 |       },
19 |       templateFile: {
20 |         type: 'string',
21 |         description:
22 |           'Path to the component template file (.html). Optional - if not provided or if the path matches typescriptFile, will extract inline template from the component. Supports both absolute and relative paths.',
23 |       },
24 |       styleFile: {
25 |         type: 'string',
26 |         description:
27 |           'Path to the component style file (.scss, .sass, .less, .css). Optional - if not provided or if the path matches typescriptFile, will extract inline styles from the component. Supports both absolute and relative paths.',
28 |       },
29 |       typescriptFile: {
30 |         type: 'string',
31 |         description:
32 |           'Path to the TypeScript component file (.ts). Supports both absolute and relative paths.',
33 |       },
34 |       dsComponentName: {
35 |         type: 'string',
36 |         description: 'The name of the design system component being used',
37 |         default: '',
38 |       },
39 |     },
40 |     required: ['saveLocation', 'typescriptFile'],
41 |   },
42 |   annotations: {
43 |     title: 'Build Component Contract',
44 |     ...COMMON_ANNOTATIONS.readOnly,
45 |   },
46 | };
47 | 
```

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

```typescript
 1 | import { validateComponentName } from '../../shared/utils/component-validation.js';
 2 | import { getDeprecatedCssClasses } from './deprecated-css-helpers.js';
 3 | import { getComponentDocs } from './doc-helpers.js';
 4 | import { getComponentPathsInfo } from './paths-helpers.js';
 5 | 
 6 | export interface ComponentMetadataParams {
 7 |   componentName: string;
 8 |   storybookDocsRoot?: string;
 9 |   deprecatedCssClassesPath?: string;
10 |   uiRoot?: string;
11 |   cwd?: string;
12 | }
13 | 
14 | export interface ComponentMetadataResult {
15 |   sourcePath: string | null;
16 |   importPath: string | null;
17 |   tagName: string | null;
18 |   api: string | null;
19 |   overview: string | null;
20 |   deprecatedCssClasses: string;
21 | }
22 | 
23 | export async function analyzeComponentMetadata(
24 |   params: ComponentMetadataParams,
25 | ): Promise<ComponentMetadataResult> {
26 |   validateComponentName(params.componentName);
27 | 
28 |   const storybookDocsRoot = params.storybookDocsRoot || 'docs';
29 |   const deprecatedCssClassesPath =
30 |     params.deprecatedCssClassesPath || 'deprecated-css-classes.js';
31 |   const uiRoot = params.uiRoot || 'libs/ui';
32 |   const cwd = params.cwd || process.cwd();
33 | 
34 |   const documentation = getComponentDocs(
35 |     params.componentName,
36 |     storybookDocsRoot,
37 |   );
38 |   const deprecatedCssClassesList = await getDeprecatedCssClasses(
39 |     params.componentName,
40 |     deprecatedCssClassesPath,
41 |     cwd,
42 |   );
43 |   const componentPaths = getComponentPathsInfo(
44 |     params.componentName,
45 |     uiRoot,
46 |     cwd,
47 |   );
48 | 
49 |   return {
50 |     sourcePath: componentPaths.srcPath,
51 |     importPath: componentPaths.importPath,
52 |     tagName: documentation.tagName,
53 |     api: documentation.api,
54 |     overview: documentation.overview,
55 |     deprecatedCssClasses: JSON.stringify(deprecatedCssClassesList),
56 |   };
57 | }
58 | 
```

--------------------------------------------------------------------------------
/packages/angular-mcp-server/src/lib/tools/ds/project/utils/styles-report-helpers.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import * as fs from 'node:fs/promises';
 2 | import * as path from 'node:path';
 3 | import { parseStylesheet, visitEachChild } from '@push-based/styles-ast-utils';
 4 | import { findAllFiles } from '@push-based/utils';
 5 | import type { Rule } from 'postcss';
 6 | 
 7 | export interface StyleFileReport {
 8 |   filePath: string;
 9 |   foundClasses: {
10 |     className: string;
11 |     selector: string;
12 |     lineNumber?: number;
13 |   }[];
14 | }
15 | 
16 | const STYLE_EXT = new Set(['.css', '.scss', '.sass', '.less']);
17 | 
18 | const isStyleFile = (f: string) => STYLE_EXT.has(path.extname(f).toLowerCase());
19 | const escapeRegex = (s: string) => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
20 | 
21 | export async function findStyleFiles(dir: string): Promise<string[]> {
22 |   const files: string[] = [];
23 |   for await (const file of findAllFiles(dir, isStyleFile)) {
24 |     files.push(file);
25 |   }
26 |   return files;
27 | }
28 | 
29 | export async function analyzeStyleFile(
30 |   filePath: string,
31 |   deprecated: string[],
32 | ): Promise<StyleFileReport> {
33 |   const css = await fs.readFile(filePath, 'utf8');
34 |   const { root } = await parseStylesheet(css, filePath);
35 | 
36 |   const found: StyleFileReport['foundClasses'] = [];
37 |   const master = new RegExp(
38 |     `\\.(${deprecated.map(escapeRegex).join('|')})(?![\\w-])`,
39 |     'g',
40 |   );
41 | 
42 |   // Handle both Document_ and Root_ types
43 |   if (root.type !== 'root') {
44 |     return { filePath, foundClasses: found };
45 |   }
46 | 
47 |   visitEachChild(root, {
48 |     visitRule(rule: Rule) {
49 |       let match;
50 |       while ((match = master.exec(rule.selector)) !== null) {
51 |         found.push({
52 |           className: match[1],
53 |           selector: rule.selector,
54 |           lineNumber: rule.source?.start?.line,
55 |         });
56 |       }
57 |       master.lastIndex = 0;
58 |     },
59 |   });
60 | 
61 |   return { filePath, foundClasses: found };
62 | }
63 | 
```

--------------------------------------------------------------------------------
/eslint.config.mjs:
--------------------------------------------------------------------------------

```
 1 | import nx from '@nx/eslint-plugin';
 2 | import unicorn from 'eslint-plugin-unicorn';
 3 | import tseslint from 'typescript-eslint';
 4 | 
 5 | export default tseslint.config(
 6 |   // Base Nx configurations
 7 |   ...nx.configs['flat/base'],
 8 |   ...nx.configs['flat/typescript'],
 9 |   ...nx.configs['flat/javascript'],
10 | 
11 |   // Global ignores
12 |   {
13 |     ignores: [
14 |       '**/dist',
15 |       '**/vite.config.*.timestamp*',
16 |       '**/vitest.config.*.timestamp*',
17 |       '**/mocks/**',
18 |       '**/fixtures/**',
19 |       '**/__snapshot__/**',
20 |       '**/__snapshots__/**',
21 |       '**/__tests__/**',
22 |       '**/__mocks__/**',
23 |       '**/test-fixtures/**',
24 |       '**/e2e/fixtures/**',
25 |     ],
26 |   },
27 | 
28 |   // Nx module boundaries and TypeScript rules
29 |   {
30 |     files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'],
31 |     rules: {
32 |       '@nx/enforce-module-boundaries': [
33 |         'error',
34 |         {
35 |           enforceBuildableLibDependency: true,
36 |           allow: ['^.*/eslint(\\.base)?\\.config\\.[cm]?js$'],
37 |           depConstraints: [
38 |             {
39 |               sourceTag: '*',
40 |               onlyDependOnLibsWithTags: ['*'],
41 |             },
42 |           ],
43 |         },
44 |       ],
45 |       '@typescript-eslint/no-unused-vars': [
46 |         'error',
47 |         {
48 |           argsIgnorePattern: '^_',
49 |           destructuredArrayIgnorePattern: '^_',
50 |           ignoreRestSiblings: true,
51 |         },
52 |       ],
53 |     },
54 |   },
55 | 
56 |   // Unicorn plugin rules
57 |   {
58 |     files: [
59 |       '**/*.ts',
60 |       '**/*.tsx',
61 |       '**/*.cts',
62 |       '**/*.mts',
63 |       '**/*.js',
64 |       '**/*.jsx',
65 |       '**/*.cjs',
66 |       '**/*.mjs',
67 |     ],
68 |     plugins: {
69 |       unicorn: unicorn,
70 |     },
71 |     rules: {
72 |       'unicorn/prefer-top-level-await': 'error',
73 |       'unicorn/catch-error-name': ['error', { name: 'ctx' }],
74 |     },
75 |   },
76 | );
77 | 
```

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

```typescript
 1 | import * as fs from 'fs';
 2 | import { resolveCrossPlatformPath } from '../../shared/utils/cross-platform-path.js';
 3 | import { loadDefaultExport } from '@push-based/utils';
 4 | 
 5 | export interface DeprecatedCssComponent {
 6 |   componentName: string;
 7 |   deprecatedCssClasses: string[];
 8 | }
 9 | 
10 | /**
11 |  * Retrieves deprecated CSS classes for a specific component from a configuration file
12 |  * @param componentName - The name of the component to get deprecated classes for
13 |  * @param deprecatedCssClassesPath - Path to the file containing deprecated CSS classes configuration
14 |  * @param cwd - Current working directory
15 |  * @returns Array of deprecated CSS classes for the component
16 |  * @throws Error if file not found, invalid format, or component not found
17 |  */
18 | export async function getDeprecatedCssClasses(
19 |   componentName: string,
20 |   deprecatedCssClassesPath: string,
21 |   cwd: string,
22 | ): Promise<string[]> {
23 |   if (
24 |     !deprecatedCssClassesPath ||
25 |     typeof deprecatedCssClassesPath !== 'string'
26 |   ) {
27 |     throw new Error('deprecatedCssClassesPath must be a string path');
28 |   }
29 | 
30 |   const absPath = resolveCrossPlatformPath(cwd, deprecatedCssClassesPath);
31 |   if (!fs.existsSync(absPath)) {
32 |     throw new Error(`File not found at deprecatedCssClassesPath: ${absPath}`);
33 |   }
34 | 
35 |   const dsComponents =
36 |     await loadDefaultExport<DeprecatedCssComponent[]>(absPath);
37 | 
38 |   if (!Array.isArray(dsComponents)) {
39 |     throw new Error('Invalid export: expected dsComponents to be an array');
40 |   }
41 | 
42 |   const componentData = dsComponents.find(
43 |     (item) => item.componentName === componentName,
44 |   );
45 | 
46 |   if (!componentData) {
47 |     throw new Error(
48 |       `No deprecated classes found for component: ${componentName}`,
49 |     );
50 |   }
51 | 
52 |   return componentData.deprecatedCssClasses;
53 | }
54 | 
```

--------------------------------------------------------------------------------
/testing/utils/src/lib/e2e-setup.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import path from 'node:path';
 2 | import fs from 'node:fs/promises';
 3 | 
 4 | export function getE2eAppProjectName(): string | undefined {
 5 |   const e2eProjectName = process.env['NX_TASK_TARGET_PROJECT'] ?? '';
 6 |   if (e2eProjectName == null) {
 7 |     console.warn('NX_TASK_TARGET_PROJECT is not set.');
 8 |   }
 9 |   return e2eProjectName ? `${e2eProjectName}-app` : undefined;
10 | }
11 | 
12 | export async function setupE2eApp(
13 |   fixtureProjectName: string,
14 |   e2eFixtures?: string,
15 | ) {
16 |   const targetProjectName =
17 |     getE2eAppProjectName() ?? fixtureProjectName + '-e2e-app';
18 |   const fixture = path.join(
19 |     __dirname,
20 |     `../../../e2e/fixtures/${fixtureProjectName}`,
21 |   );
22 |   const target = path.join(
23 |     __dirname,
24 |     `../../../e2e/__test__/${targetProjectName}`,
25 |   );
26 |   try {
27 |     await fs.stat(fixture);
28 |   } catch (ctx) {
29 |     console.warn(
30 |       `Fixture folder not found. Did you change the file or move it? Error: ${
31 |         (ctx as Error).message
32 |       }`,
33 |     );
34 |     return;
35 |   }
36 | 
37 |   // copy fixtures folder
38 |   await fs.rm(target, { recursive: true, force: true });
39 |   await fs.cp(fixture, target, {
40 |     recursive: true,
41 |     force: true,
42 |     filter(source: string, _: string): boolean | Promise<boolean> {
43 |       return !source.includes('node_modules') && !source.includes('dist');
44 |     },
45 |   });
46 | 
47 |   // adjust package.json#nx to new location and rename project
48 |   const packageJson = (
49 |     await fs.readFile(path.join(target, 'package.json'), 'utf-8')
50 |   ).toString();
51 |   await fs.writeFile(
52 |     path.join(target, 'package.json'),
53 |     packageJson
54 |       .replaceAll('fixtures', '__test__')
55 |       .replaceAll(fixtureProjectName, targetProjectName),
56 |   );
57 |   // add e2e fixtures
58 |   if (e2eFixtures) {
59 |     await fs.cp(e2eFixtures, target, {
60 |       recursive: true,
61 |       force: true,
62 |     });
63 |   }
64 | }
65 | 
```

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

```typescript
 1 | import { CliArgsObject, ArgumentValue } from '@push-based/models';
 2 | 
 3 | /**
 4 |  * Converts an object with different types of values into an array of command-line arguments.
 5 |  *
 6 |  * @example
 7 |  * const args = objectToCliArgs({
 8 |  *   _: ['node', 'index.js'], // node index.js
 9 |  *   name: 'Juanita', // --name=Juanita
10 |  *   formats: ['json', 'md'] // --format=json --format=md
11 |  * });
12 |  */
13 | export function objectToCliArgs<
14 |   T extends object = Record<string, ArgumentValue>,
15 | >(params?: CliArgsObject<T>): string[] {
16 |   if (!params) {
17 |     return [];
18 |   }
19 | 
20 |   return Object.entries(params).flatMap(([key, value]) => {
21 |     // process/file/script
22 |     if (key === '_') {
23 |       return Array.isArray(value) ? value : [`${value}`];
24 |     }
25 |     const prefix = key.length === 1 ? '-' : '--';
26 |     // "-*" arguments (shorthands)
27 |     if (Array.isArray(value)) {
28 |       return value.map((v) => `${prefix}${key}="${v}"`);
29 |     }
30 |     // "--*" arguments ==========
31 | 
32 |     if (Array.isArray(value)) {
33 |       return value.map((v) => `${prefix}${key}="${v}"`);
34 |     }
35 | 
36 |     if (typeof value === 'object') {
37 |       return Object.entries(value as Record<string, ArgumentValue>).flatMap(
38 |         // transform nested objects to the dot notation `key.subkey`
39 |         ([k, v]) => objectToCliArgs({ [`${key}.${k}`]: v }),
40 |       );
41 |     }
42 | 
43 |     if (typeof value === 'string') {
44 |       return [`${prefix}${key}="${value}"`];
45 |     }
46 | 
47 |     if (typeof value === 'number') {
48 |       return [`${prefix}${key}=${value}`];
49 |     }
50 | 
51 |     if (typeof value === 'boolean') {
52 |       return [`${prefix}${value ? '' : 'no-'}${key}`];
53 |     }
54 | 
55 |     throw new Error(`Unsupported type ${typeof value} for key ${key}`);
56 |   });
57 | }
58 | 
59 | export function calcDuration(start: number, stop?: number): number {
60 |   return Math.round((stop ?? performance.now()) - start);
61 | }
62 | 
```

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

```typescript
 1 | import {
 2 |   createHandler,
 3 |   BaseHandlerOptions,
 4 |   RESULT_FORMATTERS,
 5 | } from '../shared/utils/handler-helpers.js';
 6 | import {
 7 |   createViolationReportingSchema,
 8 |   COMMON_ANNOTATIONS,
 9 | } from '../shared/models/schema-helpers.js';
10 | import { analyzeViolationsBase } from '../shared/violation-analysis/base-analyzer.js';
11 | import { formatViolations } from '../shared/violation-analysis/formatters.js';
12 | import { ViolationResult } from './models/types.js';
13 | 
14 | interface ReportViolationsOptions extends BaseHandlerOptions {
15 |   directory: string;
16 |   componentName: string;
17 |   groupBy?: 'file' | 'folder';
18 | }
19 | 
20 | export const reportViolationsSchema = {
21 |   name: 'report-violations',
22 |   description: `Report deprecated DS CSS usage in a directory with configurable grouping format.`,
23 |   inputSchema: createViolationReportingSchema(),
24 |   annotations: {
25 |     title: 'Report Violations',
26 |     ...COMMON_ANNOTATIONS.readOnly,
27 |   },
28 | };
29 | 
30 | export const reportViolationsHandler = createHandler<
31 |   ReportViolationsOptions,
32 |   string[]
33 | >(
34 |   reportViolationsSchema.name,
35 |   async (params) => {
36 |     // Default to 'file' grouping if not specified
37 |     const groupBy = params.groupBy || 'file';
38 | 
39 |     const result = await analyzeViolationsBase<ViolationResult>(params);
40 | 
41 |     const formattedContent = formatViolations(result, params.directory, {
42 |       groupBy,
43 |     });
44 | 
45 |     // Extract text content from the formatted violations
46 |     const violationLines = formattedContent.map((item) => {
47 |       if (item.type === 'text') {
48 |         return item.text;
49 |       }
50 |       return String(item);
51 |     });
52 | 
53 |     return violationLines;
54 |   },
55 |   (result) => RESULT_FORMATTERS.list(result, 'Design System Violations:'),
56 | );
57 | 
58 | export const reportViolationsTools = [
59 |   {
60 |     schema: reportViolationsSchema,
61 |     handler: reportViolationsHandler,
62 |   },
63 | ];
64 | 
```

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

```markdown
 1 | # TypeScript AST Utils
 2 | 
 3 | Comprehensive **TypeScript AST manipulation utilities** for working with TypeScript compiler API decorators, nodes, and source file analysis.
 4 | 
 5 | ## Minimal usage
 6 | 
 7 | ```ts
 8 | import {
 9 |   isComponentDecorator,
10 |   getDecorators,
11 |   removeQuotes,
12 | } from '@push-based/typescript-ast-utils';
13 | import * as ts from 'typescript';
14 | 
15 | // Check if a decorator is a Component decorator
16 | const isComponent = isComponentDecorator(decorator);
17 | 
18 | // Get all decorators from a node
19 | const decorators = getDecorators(classNode);
20 | 
21 | // Remove quotes from a string literal node
22 | const cleanText = removeQuotes(stringNode, sourceFile);
23 | ```
24 | 
25 | ## Key Features
26 | 
27 | - **Decorator Analysis**: Utilities for identifying and working with TypeScript decorators
28 | - **Node Inspection**: Functions to safely extract decorators from AST nodes
29 | - **String Processing**: Tools for cleaning quoted strings from AST nodes
30 | - **Type Guards**: Type-safe functions for checking node properties
31 | - **Cross-Version Compatibility**: Handles different TypeScript compiler API versions
32 | 
33 | ## Use Cases
34 | 
35 | - **Angular Component Analysis**: Identify `@Component` decorators in Angular applications
36 | - **AST Traversal**: Safely navigate TypeScript abstract syntax trees
37 | - **Code Analysis Tools**: Build static analysis tools for TypeScript codebases
38 | - **Decorator Processing**: Extract and process decorator metadata
39 | - **Source Code Transformation**: Clean and manipulate string literals from source files
40 | 
41 | ## Documentation map
42 | 
43 | | Doc                            | What you'll find                            |
44 | | ------------------------------ | ------------------------------------------- |
45 | | [FUNCTIONS.md](./FUNCTIONS.md) | A–Z quick reference for every public symbol |
46 | | [EXAMPLES.md](./EXAMPLES.md)   | Runnable scenarios with expected output     |
47 | 
48 | ```
49 | 
50 | ```
51 | 
```

--------------------------------------------------------------------------------
/packages/angular-mcp-server/src/lib/tools/ds/project/get-project-dependencies.tool.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { ToolSchemaOptions } from '@push-based/models';
 2 | import {
 3 |   createHandler,
 4 |   BaseHandlerOptions,
 5 | } from '../shared/utils/handler-helpers.js';
 6 | import {
 7 |   createProjectAnalysisSchema,
 8 |   COMMON_ANNOTATIONS,
 9 | } from '../shared/models/schema-helpers.js';
10 | import { analyzeProjectDependencies } from './utils/dependencies-helpers.js';
11 | import { validateComponentName } from '../shared/utils/component-validation.js';
12 | 
13 | interface ProjectDependenciesOptions extends BaseHandlerOptions {
14 |   directory: string;
15 |   componentName?: string;
16 |   workspaceRoot?: string;
17 |   uiRoot?: string;
18 | }
19 | 
20 | export const getProjectDependenciesSchema: ToolSchemaOptions = {
21 |   name: 'get-project-dependencies',
22 |   description: `
23 |     Analyze project dependencies and detect if library is buildable/publishable.
24 |     Checks for peer dependencies and validates import paths for DS components.
25 |   `,
26 |   inputSchema: createProjectAnalysisSchema({
27 |     componentName: {
28 |       type: 'string',
29 |       description:
30 |         'Optional component name to validate import path for (e.g., DsButton)',
31 |     },
32 |   }),
33 |   annotations: {
34 |     title: 'Get Project Dependencies',
35 |     ...COMMON_ANNOTATIONS.readOnly,
36 |   },
37 | };
38 | 
39 | export const getProjectDependenciesHandler = createHandler<
40 |   ProjectDependenciesOptions,
41 |   any
42 | >(
43 |   getProjectDependenciesSchema.name,
44 |   async (params, { cwd, workspaceRoot, uiRoot }) => {
45 |     const { directory, componentName } = params;
46 | 
47 |     if (componentName) {
48 |       validateComponentName(componentName);
49 |     }
50 | 
51 |     return await analyzeProjectDependencies(
52 |       cwd,
53 |       directory,
54 |       componentName,
55 |       workspaceRoot,
56 |       uiRoot,
57 |     );
58 |   },
59 |   (result) => [JSON.stringify(result, null, 2)],
60 | );
61 | 
62 | export const getProjectDependenciesTools = [
63 |   {
64 |     schema: getProjectDependenciesSchema,
65 |     handler: getProjectDependenciesHandler,
66 |   },
67 | ];
68 | 
```

--------------------------------------------------------------------------------
/packages/shared/utils/src/lib/format-command-log.integration.test.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import path from 'node:path';
 2 | import { describe, expect, it } from 'vitest';
 3 | import { removeColorCodes } from '@push-based/testing-utils';
 4 | import { formatCommandLog } from './format-command-log.js';
 5 | 
 6 | describe('formatCommandLog', () => {
 7 |   it('should format simple command', () => {
 8 |     const result = removeColorCodes(
 9 |       formatCommandLog('npx', ['command', '--verbose']),
10 |     );
11 | 
12 |     expect(result).toBe('$ npx command --verbose');
13 |   });
14 | 
15 |   it('should format simple command with explicit process.cwd()', () => {
16 |     const result = removeColorCodes(
17 |       formatCommandLog('npx', ['command', '--verbose'], process.cwd()),
18 |     );
19 | 
20 |     expect(result).toBe('$ npx command --verbose');
21 |   });
22 | 
23 |   it('should format simple command with relative cwd', () => {
24 |     const result = removeColorCodes(
25 |       formatCommandLog('npx', ['command', '--verbose'], './wololo'),
26 |     );
27 | 
28 |     expect(result).toBe(`wololo $ npx command --verbose`);
29 |   });
30 | 
31 |   it('should format simple command with absolute non-current path converted to relative', () => {
32 |     const result = removeColorCodes(
33 |       formatCommandLog(
34 |         'npx',
35 |         ['command', '--verbose'],
36 |         path.join(process.cwd(), 'tmp'),
37 |       ),
38 |     );
39 |     expect(result).toBe('tmp $ npx command --verbose');
40 |   });
41 | 
42 |   it('should format simple command with relative cwd in parent folder', () => {
43 |     const result = removeColorCodes(
44 |       formatCommandLog('npx', ['command', '--verbose'], '..'),
45 |     );
46 | 
47 |     expect(result).toBe(`.. $ npx command --verbose`);
48 |   });
49 | 
50 |   it('should format simple command using relative path to parent directory', () => {
51 |     const result = removeColorCodes(
52 |       formatCommandLog(
53 |         'npx',
54 |         ['command', '--verbose'],
55 |         path.dirname(process.cwd()),
56 |       ),
57 |     );
58 | 
59 |     expect(result).toBe('.. $ npx command --verbose');
60 |   });
61 | });
62 | 
```

--------------------------------------------------------------------------------
/packages/minimal-repo/packages/application/src/app/components/refactoring-tests/group-3/lazy-loader-3.component.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import {
 2 |   Component,
 3 |   ViewContainerRef,
 4 |   ComponentRef,
 5 |   OnInit,
 6 | } from '@angular/core';
 7 | 
 8 | @Component({
 9 |   selector: 'app-lazy-loader',
10 |   template: `
11 |     <div>
12 |       <h2>Lazy Component Loader</h2>
13 |       <button (click)="loadComponent()" [disabled]="isLoading">
14 |         {{ isLoading ? 'Loading...' : 'Load External Assets Component' }}
15 |       </button>
16 |       <div #dynamicContainer></div>
17 |     </div>
18 |   `,
19 |   styles: [
20 |     `
21 |       button {
22 |         padding: 10px 20px;
23 |         margin: 10px 0;
24 |         background-color: #007bff;
25 |         color: white;
26 |         border: none;
27 |         border-radius: 4px;
28 |         cursor: pointer;
29 |       }
30 |       button:disabled {
31 |         background-color: #6c757d;
32 |         cursor: not-allowed;
33 |       }
34 |     `,
35 |   ],
36 | })
37 | export class LazyLoaderComponent3 implements OnInit {
38 |   isLoading = false;
39 |   private componentRef?: ComponentRef<any>;
40 | 
41 |   constructor(private viewContainerRef: ViewContainerRef) {}
42 | 
43 |   ngOnInit() {
44 |     console.log('LazyLoaderComponent initialized');
45 |   }
46 | 
47 |   async loadComponent() {
48 |     if (this.isLoading || this.componentRef) {
49 |       return;
50 |     }
51 | 
52 |     this.isLoading = true;
53 | 
54 |     try {
55 |       // Dynamic import statement - this is the key part for lazy loading
56 |       const { BadMixedExternalAssetsComponent3 } = await import(
57 |         './bad-mixed-external-assets-3.component'
58 |       );
59 | 
60 |       // Clear the container
61 |       this.viewContainerRef.clear();
62 | 
63 |       // Create the component dynamically
64 |       this.componentRef = this.viewContainerRef.createComponent(
65 |         BadMixedExternalAssetsComponent3
66 |       );
67 | 
68 |       console.log('Component loaded successfully');
69 |     } catch (error) {
70 |       console.error('Failed to load component:', error);
71 |     } finally {
72 |       this.isLoading = false;
73 |     }
74 |   }
75 | 
76 |   ngOnDestroy() {
77 |     if (this.componentRef) {
78 |       this.componentRef.destroy();
79 |     }
80 |   }
81 | }
82 | 
```

--------------------------------------------------------------------------------
/packages/shared/angular-ast-utils/src/lib/decorator-config.visitor.spec.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { classDecoratorVisitor } from './decorator-config.visitor.js';
 2 | import * as ts from 'typescript';
 3 | 
 4 | describe('DecoratorConfigVisitor', () => {
 5 |   it.skip('should not find class when it is not a class-binding', async () => {
 6 |     const sourceCode = `
 7 |   class Example {
 8 |     method() {}
 9 |   }
10 | 
11 |   function greet() {
12 |     console.log('Hello');
13 |   }
14 | 
15 |   let x = 123;
16 | `;
17 | 
18 |     const sourceFile = ts.createSourceFile(
19 |       'example.ts',
20 |       sourceCode,
21 |       ts.ScriptTarget.Latest,
22 |       true,
23 |     );
24 | 
25 |     const visitor = await classDecoratorVisitor({ sourceFile });
26 |     ts.visitEachChild(sourceFile, visitor, undefined);
27 | 
28 |     expect(visitor.components).toEqual([]);
29 |   });
30 | 
31 |   it('should not find class when it is not a class-binding', async () => {
32 |     const sourceCode = `
33 | import { Component } from '@angular/core';
34 | import { RouterOutlet } from '@angular/router';
35 | 
36 | @Component({
37 |   selector: 'app-root',
38 |   imports: [RouterOutlet],
39 |   templateUrl: './app.component.html',
40 |   styleUrls: ['./app.component.css']
41 | })
42 | export class AppComponent {
43 |   title = 'minimal';
44 | }
45 | `;
46 | 
47 |     const sourceFile = ts.createSourceFile(
48 |       'example.ts',
49 |       sourceCode,
50 |       ts.ScriptTarget.Latest,
51 |       true,
52 |     );
53 |     const visitor = await classDecoratorVisitor({ sourceFile });
54 | 
55 |     ts.visitEachChild(sourceFile, visitor, undefined);
56 | 
57 |     expect(visitor.components).toStrictEqual([
58 |       expect.objectContaining({
59 |         className: 'AppComponent',
60 |         fileName: 'example.ts',
61 |         startLine: 4,
62 |         selector: 'app-root',
63 |         imports: '[RouterOutlet]',
64 |         styleUrls: [
65 |           expect.objectContaining({
66 |             startLine: 8,
67 |             filePath: 'example.ts',
68 |           }),
69 |         ],
70 |         templateUrl: expect.objectContaining({
71 |           startLine: 7,
72 |           filePath: 'example.ts',
73 |         }),
74 |       }),
75 |     ]);
76 |   });
77 | });
78 | 
```

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

```typescript
 1 | import * as path from 'path';
 2 | import { AngularMcpServerOptions } from './angular-mcp-server-options.schema.js';
 3 | import { DsComponentsArraySchema } from './ds-components.schema.js';
 4 | import { loadDefaultExport } from '@push-based/utils';
 5 | 
 6 | export async function validateDeprecatedCssClassesFile(
 7 |   config: AngularMcpServerOptions,
 8 | ): Promise<void> {
 9 |   const relPath = config.ds.deprecatedCssClassesPath;
10 |   if (!relPath) {
11 |     // Optional parameter not provided; nothing to validate
12 |     return;
13 |   }
14 |   const deprecatedCssClassesAbsPath = path.resolve(
15 |     config.workspaceRoot,
16 |     relPath,
17 |   );
18 | 
19 |   const dsComponents = await loadDefaultExport(deprecatedCssClassesAbsPath);
20 | 
21 |   const validation = DsComponentsArraySchema.safeParse(dsComponents);
22 |   if (!validation.success) {
23 |     const actualType = Array.isArray(dsComponents)
24 |       ? 'array'
25 |       : typeof dsComponents;
26 |     const exportedValue =
27 |       dsComponents === undefined
28 |         ? 'undefined'
29 |         : dsComponents === null
30 |           ? 'null'
31 |           : JSON.stringify(dsComponents, null, 2).substring(0, 100) +
32 |             (JSON.stringify(dsComponents).length > 100 ? '...' : '');
33 | 
34 |     throw new Error(
35 |       `Invalid deprecated CSS classes configuration format in: ${deprecatedCssClassesAbsPath}\n\n` +
36 |         `Expected: Array of component objects\n` +
37 |         `Received: ${actualType}\n` +
38 |         `Value: ${exportedValue}\n\n` +
39 |         `Fix options:\n` +
40 |         `1. ES Module format:\n` +
41 |         `   export default [\n` +
42 |         `     { componentName: 'DsButton', deprecatedCssClasses: ['btn'] }\n` +
43 |         `   ];\n\n` +
44 |         `2. CommonJS format:\n` +
45 |         `   module.exports = {\n` +
46 |         `     dsComponents: [{ componentName: 'DsButton', deprecatedCssClasses: ['btn'] }]\n` +
47 |         `   };\n\n` +
48 |         `Schema errors: ${JSON.stringify(validation.error.format(), null, 2)}`,
49 |     );
50 |   }
51 | }
52 | 
```

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

```typescript
 1 | import { Component } from '@angular/core';
 2 | 
 3 | @Component({
 4 |   selector: 'app-mixed-styles',
 5 |   template: `
 6 |     <!-- ✅ Good: Using DSButton -->
 7 |     <ds-button>Good Button</ds-button>
 8 | 
 9 |     <!-- ❌ Bad: Legacy button class -->
10 |     <button class="btn btn-primary">Bad Button</button>
11 | 
12 |     <!-- ✅ Good: Using DSModal -->
13 |     <ds-modal [open]="true">
14 |       <p>Good Modal Content</p>
15 |     </ds-modal>
16 | 
17 |     <!-- ❌ Bad: Custom modal with legacy styles -->
18 |     <div class="modal">
19 |       <div class="modal-content">
20 |         <h2>Bad Modal</h2>
21 |         <p>This is a legacy modal.</p>
22 |       </div>
23 |     </div>
24 | 
25 |     <!-- ✅ Good: DSProgressBar -->
26 |     <ds-progress-bar [value]="50"></ds-progress-bar>
27 | 
28 |     <!-- ❌ Bad: Manually styled progress bar -->
29 |     <div class="progress-bar">
30 |       <div class="progress" style="width: 50%;"></div>
31 |     </div>
32 | 
33 |     <!-- ✅ Good: DSDropdown -->
34 |     <ds-dropdown [options]="['Option 1', 'Option 2']"></ds-dropdown>
35 | 
36 |     <!-- ❌ Bad: Legacy dropdown -->
37 |     <select class="dropdown">
38 |       <option>Option 1</option>
39 |       <option>Option 2</option>
40 |     </select>
41 | 
42 |     <!-- ✅ Good: Using DSAlert -->
43 |     <ds-alert type="error"> Good Alert </ds-alert>
44 | 
45 |     <!-- ❌ Bad: Manually styled alert -->
46 |     <div class="alert alert-danger">Bad Alert</div>
47 | 
48 |     <!-- ✅ Good: Using DSTooltip -->
49 |     <ds-tooltip content="Good tooltip">Hover me</ds-tooltip>
50 | 
51 |     <!-- ❌ Bad: Legacy tooltip -->
52 |     <div class="tooltip">Bad tooltip</div>
53 | 
54 |     <!-- ✅ Good: Using DSBreadcrumb -->
55 |     <ds-breadcrumb>
56 |       <ds-breadcrumb-item>Home</ds-breadcrumb-item>
57 |       <ds-breadcrumb-item>Products</ds-breadcrumb-item>
58 |       <ds-breadcrumb-item>Details</ds-breadcrumb-item>
59 |     </ds-breadcrumb>
60 | 
61 |     <!-- ❌ Bad: Manually created breadcrumb -->
62 |     <nav class="breadcrumb">
63 |       <span>Home</span> / <span>Products</span> / <span>Details</span>
64 |     </nav>
65 |   `,
66 | })
67 | export class MixedStylesComponent {}
68 | 
```

--------------------------------------------------------------------------------
/packages/shared/ds-component-coverage/mocks/fixtures/e2e/demo/src/sub-folder-1/sub-folder-2/bad-mixed.component.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { Component } from '@angular/core';
 2 | 
 3 | @Component({
 4 |   selector: 'app-mixed-styles',
 5 |   template: `
 6 |     <!-- ✅ Good: Using DSButton -->
 7 |     <ds-button>Good Button</ds-button>
 8 | 
 9 |     <!-- ❌ Bad: Legacy button class -->
10 |     <button class="btn btn-primary">Bad Button</button>
11 | 
12 |     <!-- ✅ Good: Using DSModal -->
13 |     <ds-modal [open]="true">
14 |       <p>Good Modal Content</p>
15 |     </ds-modal>
16 | 
17 |     <!-- ❌ Bad: Custom modal with legacy styles -->
18 |     <div class="modal">
19 |       <div class="modal-content">
20 |         <h2>Bad Modal</h2>
21 |         <p>This is a legacy modal.</p>
22 |       </div>
23 |     </div>
24 | 
25 |     <!-- ✅ Good: DSProgressBar -->
26 |     <ds-progress-bar [value]="50"></ds-progress-bar>
27 | 
28 |     <!-- ❌ Bad: Manually styled progress bar -->
29 |     <div class="progress-bar">
30 |       <div class="progress" style="width: 50%;"></div>
31 |     </div>
32 | 
33 |     <!-- ✅ Good: DSDropdown -->
34 |     <ds-dropdown [options]="['Option 1', 'Option 2']"></ds-dropdown>
35 | 
36 |     <!-- ❌ Bad: Legacy dropdown -->
37 |     <select class="dropdown">
38 |       <option>Option 1</option>
39 |       <option>Option 2</option>
40 |     </select>
41 | 
42 |     <!-- ✅ Good: Using DSAlert -->
43 |     <ds-alert type="error"> Good Alert </ds-alert>
44 | 
45 |     <!-- ❌ Bad: Manually styled alert -->
46 |     <div class="alert alert-danger">Bad Alert</div>
47 | 
48 |     <!-- ✅ Good: Using DSTooltip -->
49 |     <ds-tooltip content="Good tooltip">Hover me</ds-tooltip>
50 | 
51 |     <!-- ❌ Bad: Legacy tooltip -->
52 |     <div class="tooltip">Bad tooltip</div>
53 | 
54 |     <!-- ✅ Good: Using DSBreadcrumb -->
55 |     <ds-breadcrumb>
56 |       <ds-breadcrumb-item>Home</ds-breadcrumb-item>
57 |       <ds-breadcrumb-item>Products</ds-breadcrumb-item>
58 |       <ds-breadcrumb-item>Details</ds-breadcrumb-item>
59 |     </ds-breadcrumb>
60 | 
61 |     <!-- ❌ Bad: Manually created breadcrumb -->
62 |     <nav class="breadcrumb">
63 |       <span>Home</span> / <span>Products</span> / <span>Details</span>
64 |     </nav>
65 |   `,
66 | })
67 | export class MixedStylesComponent {}
68 | 
```

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

```typescript
 1 | import { Component } from '@angular/core';
 2 | 
 3 | @Component({
 4 |   selector: 'app-mixed-styles',
 5 |   template: `
 6 |     <!-- ✅ Good: Using DSButton -->
 7 |     <ds-button>Good Button</ds-button>
 8 | 
 9 |     <!-- ❌ Bad: Legacy button class -->
10 |     <button class="btn btn-primary">Bad Button</button>
11 | 
12 |     <!-- ✅ Good: Using DSModal -->
13 |     <ds-modal [open]="true">
14 |       <p>Good Modal Content</p>
15 |     </ds-modal>
16 | 
17 |     <!-- ❌ Bad: Custom modal with legacy styles -->
18 |     <div class="modal">
19 |       <div class="modal-content">
20 |         <h2>Bad Modal</h2>
21 |         <p>This is a legacy modal.</p>
22 |       </div>
23 |     </div>
24 | 
25 |     <!-- ✅ Good: DSProgressBar -->
26 |     <ds-progress-bar [value]="50"></ds-progress-bar>
27 | 
28 |     <!-- ❌ Bad: Manually styled progress bar -->
29 |     <div class="progress-bar">
30 |       <div class="progress" style="width: 50%;"></div>
31 |     </div>
32 | 
33 |     <!-- ✅ Good: DSDropdown -->
34 |     <ds-dropdown [options]="['Option 1', 'Option 2']"></ds-dropdown>
35 | 
36 |     <!-- ❌ Bad: Legacy dropdown -->
37 |     <select class="dropdown">
38 |       <option>Option 1</option>
39 |       <option>Option 2</option>
40 |     </select>
41 | 
42 |     <!-- ✅ Good: Using DSAlert -->
43 |     <ds-alert type="error"> Good Alert </ds-alert>
44 | 
45 |     <!-- ❌ Bad: Manually styled alert -->
46 |     <div class="alert alert-danger">Bad Alert</div>
47 | 
48 |     <!-- ✅ Good: Using DSTooltip -->
49 |     <ds-tooltip content="Good tooltip">Hover me</ds-tooltip>
50 | 
51 |     <!-- ❌ Bad: Legacy tooltip -->
52 |     <div class="tooltip">Bad tooltip</div>
53 | 
54 |     <!-- ✅ Good: Using DSBreadcrumb -->
55 |     <ds-breadcrumb>
56 |       <ds-breadcrumb-item>Home</ds-breadcrumb-item>
57 |       <ds-breadcrumb-item>Products</ds-breadcrumb-item>
58 |       <ds-breadcrumb-item>Details</ds-breadcrumb-item>
59 |     </ds-breadcrumb>
60 | 
61 |     <!-- ❌ Bad: Manually created breadcrumb -->
62 |     <nav class="breadcrumb">
63 |       <span>Home</span> / <span>Products</span> / <span>Details</span>
64 |     </nav>
65 |   `,
66 | })
67 | export class MixedStylesComponent1 {}
68 | 
```

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

```typescript
 1 | import { Component } from '@angular/core';
 2 | 
 3 | @Component({
 4 |   selector: 'app-mixed-styles',
 5 |   template: `
 6 |     <!-- ✅ Good: Using DSButton -->
 7 |     <ds-button>Good Button</ds-button>
 8 | 
 9 |     <!-- ❌ Bad: Legacy button class -->
10 |     <button class="btn btn-primary">Bad Button</button>
11 | 
12 |     <!-- ✅ Good: Using DSModal -->
13 |     <ds-modal [open]="true">
14 |       <p>Good Modal Content</p>
15 |     </ds-modal>
16 | 
17 |     <!-- ❌ Bad: Custom modal with legacy styles -->
18 |     <div class="modal">
19 |       <div class="modal-content">
20 |         <h2>Bad Modal</h2>
21 |         <p>This is a legacy modal.</p>
22 |       </div>
23 |     </div>
24 | 
25 |     <!-- ✅ Good: DSProgressBar -->
26 |     <ds-progress-bar [value]="50"></ds-progress-bar>
27 | 
28 |     <!-- ❌ Bad: Manually styled progress bar -->
29 |     <div class="progress-bar">
30 |       <div class="progress" style="width: 50%;"></div>
31 |     </div>
32 | 
33 |     <!-- ✅ Good: DSDropdown -->
34 |     <ds-dropdown [options]="['Option 1', 'Option 2']"></ds-dropdown>
35 | 
36 |     <!-- ❌ Bad: Legacy dropdown -->
37 |     <select class="dropdown">
38 |       <option>Option 1</option>
39 |       <option>Option 2</option>
40 |     </select>
41 | 
42 |     <!-- ✅ Good: Using DSAlert -->
43 |     <ds-alert type="error"> Good Alert </ds-alert>
44 | 
45 |     <!-- ❌ Bad: Manually styled alert -->
46 |     <div class="alert alert-danger">Bad Alert</div>
47 | 
48 |     <!-- ✅ Good: Using DSTooltip -->
49 |     <ds-tooltip content="Good tooltip">Hover me</ds-tooltip>
50 | 
51 |     <!-- ❌ Bad: Legacy tooltip -->
52 |     <div class="tooltip">Bad tooltip</div>
53 | 
54 |     <!-- ✅ Good: Using DSBreadcrumb -->
55 |     <ds-breadcrumb>
56 |       <ds-breadcrumb-item>Home</ds-breadcrumb-item>
57 |       <ds-breadcrumb-item>Products</ds-breadcrumb-item>
58 |       <ds-breadcrumb-item>Details</ds-breadcrumb-item>
59 |     </ds-breadcrumb>
60 | 
61 |     <!-- ❌ Bad: Manually created breadcrumb -->
62 |     <nav class="breadcrumb">
63 |       <span>Home</span> / <span>Products</span> / <span>Details</span>
64 |     </nav>
65 |   `,
66 | })
67 | export class MixedStylesComponent2 {}
68 | 
```

--------------------------------------------------------------------------------
/packages/shared/styles-ast-utils/src/lib/stylesheet.walk.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { Root, Rule } from 'postcss';
 2 | 
 3 | import { CssAstVisitor } from './stylesheet.visitor.js';
 4 | import { NodeType } from './types.js';
 5 | 
 6 | /**
 7 |  * Single function that traverses the AST, calling
 8 |  * specialized visitor methods as it encounters each node type.
 9 |  */
10 | export function visitEachChild<T>(root: Root, visitor: CssAstVisitor<T>) {
11 |   visitor.visitRoot?.(root);
12 | 
13 |   root.walk((node) => {
14 |     const visitMethodName = `visit${
15 |       node.type[0].toUpperCase() + node.type.slice(1)
16 |     }` as keyof CssAstVisitor<T>;
17 |     const visitMethod = visitor[visitMethodName] as
18 |       | ((node: NodeType<typeof visitMethodName>) => void)
19 |       | undefined;
20 |     visitMethod?.(node as NodeType<typeof visitMethodName>);
21 |   });
22 | }
23 | 
24 | export function visitStyleSheet<T>(root: Root, visitor: CssAstVisitor<T>) {
25 |   for (const node of root.nodes) {
26 |     switch (node.type) {
27 |       case 'rule':
28 |         visitor?.visitRule?.(node);
29 |         break;
30 |       case 'atrule':
31 |         visitor?.visitAtRule?.(node);
32 |         break;
33 |       case 'decl':
34 |         throw new Error('visit declaration not implemented');
35 |       // visitor?.visitDeclaration?.(node);
36 |       case 'comment':
37 |         visitor?.visitComment?.(node);
38 |         break;
39 |       default:
40 |         throw new Error(`Unknown node type: ${(node as Root).type}`);
41 |     }
42 |   }
43 | }
44 | 
45 | export function visitEachStyleNode<T>(
46 |   nodes: Root['nodes'],
47 |   visitor: CssAstVisitor<T>,
48 | ) {
49 |   for (const node of nodes) {
50 |     switch (node.type) {
51 |       case 'rule':
52 |         visitor?.visitRule?.(node);
53 |         visitEachStyleNode((node as Rule).nodes, visitor);
54 |         break;
55 |       case 'atrule':
56 |         visitor?.visitAtRule?.(node);
57 |         break;
58 |       case 'decl':
59 |         visitor?.visitDecl?.(node);
60 |         break;
61 |       case 'comment':
62 |         visitor?.visitComment?.(node);
63 |         break;
64 |       default:
65 |         throw new Error(`Unknown node type: ${(node as Root).type}`);
66 |     }
67 |   }
68 | }
69 | 
```

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

```typescript
 1 | import { parseStylesheet, visitEachChild } from '@push-based/styles-ast-utils';
 2 | import { selectorMatches } from './css-match.js';
 3 | import type {
 4 |   StyleDeclarations,
 5 |   DomStructure,
 6 | } from '../../shared/models/types.js';
 7 | import type { Declaration, Rule } from 'postcss';
 8 | import type { ParsedComponent } from '@push-based/angular-ast-utils';
 9 | 
10 | /**
11 |  * Collect style rules declared inline via the `styles` property of an
12 |  * `@Component` decorator and map them to the DOM snapshot that comes from the
13 |  * template.
14 |  */
15 | export async function collectInlineStyles(
16 |   component: ParsedComponent,
17 |   dom: DomStructure,
18 | ): Promise<StyleDeclarations> {
19 |   const styles: StyleDeclarations = {
20 |     // Inline styles logically live in the component TS file
21 |     sourceFile: component.fileName,
22 |     rules: {},
23 |   };
24 | 
25 |   if (!component.styles || component.styles.length === 0) {
26 |     return styles;
27 |   }
28 | 
29 |   // Combine all inline style strings into one CSS blob
30 |   const cssText = (
31 |     await Promise.all(component.styles.map((asset) => asset.parse()))
32 |   )
33 |     .map((root) => root.toString())
34 |     .join('\n');
35 | 
36 |   if (!cssText.trim()) {
37 |     return styles;
38 |   }
39 | 
40 |   const parsed = parseStylesheet(cssText, component.fileName);
41 |   if (parsed.root.type !== 'root') {
42 |     return styles;
43 |   }
44 | 
45 |   visitEachChild(parsed.root, {
46 |     visitRule: (rule: Rule) => {
47 |       const properties: Record<string, string> = {};
48 | 
49 |       rule.walkDecls?.((decl: Declaration) => {
50 |         properties[decl.prop] = decl.value;
51 |       });
52 | 
53 |       styles.rules[rule.selector] = {
54 |         appliesTo: findMatchingDomElements(rule.selector, dom),
55 |         properties,
56 |       };
57 |     },
58 |   });
59 | 
60 |   return styles;
61 | }
62 | 
63 | function findMatchingDomElements(
64 |   cssSelector: string,
65 |   dom: DomStructure,
66 | ): string[] {
67 |   return Object.entries(dom)
68 |     .filter(([domKey, element]) =>
69 |       selectorMatches(cssSelector, domKey, element),
70 |     )
71 |     .map(([domKey]) => domKey);
72 | }
73 | 
```

--------------------------------------------------------------------------------
/packages/minimal-repo/packages/application/src/app/components/refactoring-tests/bad-mixed-not-standalone.component.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { Component } from '@angular/core';
 2 | 
 3 | @Component({
 4 |   selector: 'app-mixed-styles',
 5 |   standalone: false,
 6 |   template: `
 7 |     <!-- ✅ Good: Using DSButton -->
 8 |     <ds-button>Good Button</ds-button>
 9 | 
10 |     <!-- ❌ Bad: Legacy button class -->
11 |     <button class="btn btn-primary">Bad Button</button>
12 | 
13 |     <!-- ✅ Good: Using DSModal -->
14 |     <ds-modal [open]="true">
15 |       <p>Good Modal Content</p>
16 |     </ds-modal>
17 | 
18 |     <!-- ❌ Bad: Custom modal with legacy styles -->
19 |     <div class="modal">
20 |       <div class="modal-content">
21 |         <h2>Bad Modal</h2>
22 |         <p>This is a legacy modal.</p>
23 |       </div>
24 |     </div>
25 | 
26 |     <!-- ✅ Good: DSProgressBar -->
27 |     <ds-progress-bar [value]="50"></ds-progress-bar>
28 | 
29 |     <!-- ❌ Bad: Manually styled progress bar -->
30 |     <div class="progress-bar">
31 |       <div class="progress" style="width: 50%;"></div>
32 |     </div>
33 | 
34 |     <!-- ✅ Good: DSDropdown -->
35 |     <ds-dropdown [options]="['Option 1', 'Option 2']"></ds-dropdown>
36 | 
37 |     <!-- ❌ Bad: Legacy dropdown -->
38 |     <select class="dropdown">
39 |       <option>Option 1</option>
40 |       <option>Option 2</option>
41 |     </select>
42 | 
43 |     <!-- ✅ Good: Using DSAlert -->
44 |     <ds-alert type="error"> Good Alert </ds-alert>
45 | 
46 |     <!-- ❌ Bad: Manually styled alert -->
47 |     <div class="alert alert-danger">Bad Alert</div>
48 | 
49 |     <!-- ✅ Good: Using DSTooltip -->
50 |     <ds-tooltip content="Good tooltip">Hover me</ds-tooltip>
51 | 
52 |     <!-- ❌ Bad: Legacy tooltip -->
53 |     <div class="tooltip">Bad tooltip</div>
54 | 
55 |     <!-- ✅ Good: Using DSBreadcrumb -->
56 |     <ds-breadcrumb>
57 |       <ds-breadcrumb-item>Home</ds-breadcrumb-item>
58 |       <ds-breadcrumb-item>Products</ds-breadcrumb-item>
59 |       <ds-breadcrumb-item>Details</ds-breadcrumb-item>
60 |     </ds-breadcrumb>
61 | 
62 |     <!-- ❌ Bad: Manually created breadcrumb -->
63 |     <nav class="breadcrumb">
64 |       <span>Home</span> / <span>Products</span> / <span>Details</span>
65 |     </nav>
66 |   `,
67 | })
68 | export class MixedStylesNotStandaloneComponent {}
69 | 
```

--------------------------------------------------------------------------------
/packages/minimal-repo/packages/application/src/app/components/refactoring-tests/group-1/bad-mixed-not-standalone-1.component.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { Component } from '@angular/core';
 2 | 
 3 | @Component({
 4 |   selector: 'app-mixed-styles',
 5 |   standalone: false,
 6 |   template: `
 7 |     <!-- ✅ Good: Using DSButton -->
 8 |     <ds-button>Good Button</ds-button>
 9 | 
10 |     <!-- ❌ Bad: Legacy button class -->
11 |     <button class="btn btn-primary">Bad Button</button>
12 | 
13 |     <!-- ✅ Good: Using DSModal -->
14 |     <ds-modal [open]="true">
15 |       <p>Good Modal Content</p>
16 |     </ds-modal>
17 | 
18 |     <!-- ❌ Bad: Custom modal with legacy styles -->
19 |     <div class="modal">
20 |       <div class="modal-content">
21 |         <h2>Bad Modal</h2>
22 |         <p>This is a legacy modal.</p>
23 |       </div>
24 |     </div>
25 | 
26 |     <!-- ✅ Good: DSProgressBar -->
27 |     <ds-progress-bar [value]="50"></ds-progress-bar>
28 | 
29 |     <!-- ❌ Bad: Manually styled progress bar -->
30 |     <div class="progress-bar">
31 |       <div class="progress" style="width: 50%;"></div>
32 |     </div>
33 | 
34 |     <!-- ✅ Good: DSDropdown -->
35 |     <ds-dropdown [options]="['Option 1', 'Option 2']"></ds-dropdown>
36 | 
37 |     <!-- ❌ Bad: Legacy dropdown -->
38 |     <select class="dropdown">
39 |       <option>Option 1</option>
40 |       <option>Option 2</option>
41 |     </select>
42 | 
43 |     <!-- ✅ Good: Using DSAlert -->
44 |     <ds-alert type="error"> Good Alert </ds-alert>
45 | 
46 |     <!-- ❌ Bad: Manually styled alert -->
47 |     <div class="alert alert-danger">Bad Alert</div>
48 | 
49 |     <!-- ✅ Good: Using DSTooltip -->
50 |     <ds-tooltip content="Good tooltip">Hover me</ds-tooltip>
51 | 
52 |     <!-- ❌ Bad: Legacy tooltip -->
53 |     <div class="tooltip">Bad tooltip</div>
54 | 
55 |     <!-- ✅ Good: Using DSBreadcrumb -->
56 |     <ds-breadcrumb>
57 |       <ds-breadcrumb-item>Home</ds-breadcrumb-item>
58 |       <ds-breadcrumb-item>Products</ds-breadcrumb-item>
59 |       <ds-breadcrumb-item>Details</ds-breadcrumb-item>
60 |     </ds-breadcrumb>
61 | 
62 |     <!-- ❌ Bad: Manually created breadcrumb -->
63 |     <nav class="breadcrumb">
64 |       <span>Home</span> / <span>Products</span> / <span>Details</span>
65 |     </nav>
66 |   `,
67 | })
68 | export class MixedStylesNotStandaloneComponent1 {}
69 | 
```

--------------------------------------------------------------------------------
/packages/minimal-repo/packages/application/src/app/components/refactoring-tests/group-2/bad-mixed-not-standalone-2.component.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { Component } from '@angular/core';
 2 | 
 3 | @Component({
 4 |   selector: 'app-mixed-styles',
 5 |   standalone: false,
 6 |   template: `
 7 |     <!-- ✅ Good: Using DSButton -->
 8 |     <ds-button>Good Button</ds-button>
 9 | 
10 |     <!-- ❌ Bad: Legacy button class -->
11 |     <button class="btn btn-primary">Bad Button</button>
12 | 
13 |     <!-- ✅ Good: Using DSModal -->
14 |     <ds-modal [open]="true">
15 |       <p>Good Modal Content</p>
16 |     </ds-modal>
17 | 
18 |     <!-- ❌ Bad: Custom modal with legacy styles -->
19 |     <div class="modal">
20 |       <div class="modal-content">
21 |         <h2>Bad Modal</h2>
22 |         <p>This is a legacy modal.</p>
23 |       </div>
24 |     </div>
25 | 
26 |     <!-- ✅ Good: DSProgressBar -->
27 |     <ds-progress-bar [value]="50"></ds-progress-bar>
28 | 
29 |     <!-- ❌ Bad: Manually styled progress bar -->
30 |     <div class="progress-bar">
31 |       <div class="progress" style="width: 50%;"></div>
32 |     </div>
33 | 
34 |     <!-- ✅ Good: DSDropdown -->
35 |     <ds-dropdown [options]="['Option 1', 'Option 2']"></ds-dropdown>
36 | 
37 |     <!-- ❌ Bad: Legacy dropdown -->
38 |     <select class="dropdown">
39 |       <option>Option 1</option>
40 |       <option>Option 2</option>
41 |     </select>
42 | 
43 |     <!-- ✅ Good: Using DSAlert -->
44 |     <ds-alert type="error"> Good Alert </ds-alert>
45 | 
46 |     <!-- ❌ Bad: Manually styled alert -->
47 |     <div class="alert alert-danger">Bad Alert</div>
48 | 
49 |     <!-- ✅ Good: Using DSTooltip -->
50 |     <ds-tooltip content="Good tooltip">Hover me</ds-tooltip>
51 | 
52 |     <!-- ❌ Bad: Legacy tooltip -->
53 |     <div class="tooltip">Bad tooltip</div>
54 | 
55 |     <!-- ✅ Good: Using DSBreadcrumb -->
56 |     <ds-breadcrumb>
57 |       <ds-breadcrumb-item>Home</ds-breadcrumb-item>
58 |       <ds-breadcrumb-item>Products</ds-breadcrumb-item>
59 |       <ds-breadcrumb-item>Details</ds-breadcrumb-item>
60 |     </ds-breadcrumb>
61 | 
62 |     <!-- ❌ Bad: Manually created breadcrumb -->
63 |     <nav class="breadcrumb">
64 |       <span>Home</span> / <span>Products</span> / <span>Details</span>
65 |     </nav>
66 |   `,
67 | })
68 | export class MixedStylesNotStandaloneComponent2 {}
69 | 
```

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

```typescript
 1 | import { Component } from '@angular/core';
 2 | 
 3 | @Component({
 4 |   selector: 'app-mixed-styles',
 5 |   standalone: false,
 6 |   template: `
 7 |     <!-- ✅ Good: Using DSButton -->
 8 |     <ds-button>Good Button</ds-button>
 9 | 
10 |     <!-- ❌ Bad: Legacy button class -->
11 |     <button class="btn btn-primary">Bad Button</button>
12 | 
13 |     <!-- ✅ Good: Using DSModal -->
14 |     <ds-modal [open]="true">
15 |       <p>Good Modal Content</p>
16 |     </ds-modal>
17 | 
18 |     <!-- ❌ Bad: Custom modal with legacy styles -->
19 |     <div class="modal">
20 |       <div class="modal-content">
21 |         <h2>Bad Modal</h2>
22 |         <p>This is a legacy modal.</p>
23 |       </div>
24 |     </div>
25 | 
26 |     <!-- ✅ Good: DSProgressBar -->
27 |     <ds-progress-bar [value]="50"></ds-progress-bar>
28 | 
29 |     <!-- ❌ Bad: Manually styled progress bar -->
30 |     <div class="progress-bar">
31 |       <div class="progress" style="width: 50%;"></div>
32 |     </div>
33 | 
34 |     <!-- ✅ Good: DSDropdown -->
35 |     <ds-dropdown [options]="['Option 1', 'Option 2']"></ds-dropdown>
36 | 
37 |     <!-- ❌ Bad: Legacy dropdown -->
38 |     <select class="dropdown">
39 |       <option>Option 1</option>
40 |       <option>Option 2</option>
41 |     </select>
42 | 
43 |     <!-- ✅ Good: Using DSAlert -->
44 |     <ds-alert type="error"> Good Alert </ds-alert>
45 | 
46 |     <!-- ❌ Bad: Manually styled alert -->
47 |     <div class="alert alert-danger">Bad Alert</div>
48 | 
49 |     <!-- ✅ Good: Using DSTooltip -->
50 |     <ds-tooltip content="Good tooltip">Hover me</ds-tooltip>
51 | 
52 |     <!-- ❌ Bad: Legacy tooltip -->
53 |     <div class="tooltip">Bad tooltip</div>
54 | 
55 |     <!-- ✅ Good: Using DSBreadcrumb -->
56 |     <ds-breadcrumb>
57 |       <ds-breadcrumb-item>Home</ds-breadcrumb-item>
58 |       <ds-breadcrumb-item>Products</ds-breadcrumb-item>
59 |       <ds-breadcrumb-item>Details</ds-breadcrumb-item>
60 |     </ds-breadcrumb>
61 | 
62 |     <!-- ❌ Bad: Manually created breadcrumb -->
63 |     <nav class="breadcrumb">
64 |       <span>Home</span> / <span>Products</span> / <span>Details</span>
65 |     </nav>
66 |   `,
67 | })
68 | export class MixedStylesNotStandaloneComponent3 {}
69 | 
```

--------------------------------------------------------------------------------
/nx.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "$schema": "./node_modules/nx/schemas/nx-schema.json",
 3 |   "namedInputs": {
 4 |     "default": ["{projectRoot}/**/*", "sharedGlobals"],
 5 |     "production": [
 6 |       "default",
 7 |       "!{projectRoot}/.eslintrc.json",
 8 |       "!{projectRoot}/eslint.config.mjs",
 9 |       "!{projectRoot}/**/?(*.)+(spec|test).[jt]s?(x)?(.snap)",
10 |       "!{projectRoot}/tsconfig.spec.json",
11 |       "!{projectRoot}/jest.config.[jt]s",
12 |       "!{projectRoot}/src/test-setup.[jt]s",
13 |       "!{projectRoot}/test-setup.[jt]s"
14 |     ],
15 |     "sharedGlobals": ["{workspaceRoot}/.github/workflows/ci.yml"]
16 |   },
17 |   "plugins": [
18 |     {
19 |       "plugin": "@nx/js/typescript",
20 |       "options": {
21 |         "typecheck": {
22 |           "targetName": "typecheck"
23 |         },
24 |         "build": {
25 |           "targetName": "build",
26 |           "configName": "tsconfig.lib.json",
27 |           "buildDepsName": "build-deps",
28 |           "watchDepsName": "watch-deps"
29 |         }
30 |       }
31 |     },
32 |     {
33 |       "plugin": "@nx/webpack/plugin",
34 |       "options": {
35 |         "buildTargetName": "build",
36 |         "serveTargetName": "serve",
37 |         "previewTargetName": "preview",
38 |         "buildDepsTargetName": "build-deps",
39 |         "watchDepsTargetName": "watch-deps"
40 |       }
41 |     },
42 |     {
43 |       "plugin": "@nx/eslint/plugin",
44 |       "options": {
45 |         "targetName": "lint"
46 |       }
47 |     },
48 |     {
49 |       "plugin": "@nx/vite/plugin",
50 |       "options": {
51 |         "buildTargetName": "build",
52 |         "testTargetName": "test",
53 |         "serveTargetName": "serve",
54 |         "devTargetName": "dev",
55 |         "previewTargetName": "preview",
56 |         "serveStaticTargetName": "serve-static",
57 |         "typecheckTargetName": "typecheck",
58 |         "buildDepsTargetName": "build-deps",
59 |         "watchDepsTargetName": "watch-deps"
60 |       }
61 |     }
62 |   ],
63 |   "targetDefaults": {
64 |     "@nx/js:swc": {
65 |       "cache": true,
66 |       "dependsOn": ["^build"],
67 |       "inputs": ["production", "^production"]
68 |     },
69 |     "test": {
70 |       "dependsOn": ["^build"]
71 |     }
72 |   }
73 | }
74 | 
```

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

```markdown
 1 | ## Inputs
 2 | 
 3 | ### `ds-modal-header`
 4 | 
 5 | | Name      | Type                                                               | Default     | Description                           |
 6 | | --------- | ------------------------------------------------------------------ | ----------- | ------------------------------------- |
 7 | | `variant` | `'surface-lowest' \| 'surface-low' \| 'surface' \| 'surface-high'` | `'surface'` | Background style for the modal header |
 8 | 
 9 | Other components (`ds-modal`, `ds-modal-content`, `ds-modal-header-drag`) do not define any `@Input()` bindings.
10 | 
11 | ---
12 | 
13 | ## Outputs
14 | 
15 | None of the modal-related components emit Angular `@Output()` events.
16 | 
17 | ---
18 | 
19 | ## Content Projection
20 | 
21 | ### `ds-modal`
22 | 
23 | Supports default slot:
24 | 
25 | ```html
26 | <ds-modal>
27 |   <ds-modal-header>...</ds-modal-header>
28 |   <ds-modal-content>...</ds-modal-content>
29 | </ds-modal>
30 | ```
31 | 
32 | ### `ds-modal-header`
33 | 
34 | Defines multiple named slots:
35 | 
36 | ```html
37 | <ds-modal-header>
38 |   <span slot="start">Back</span>
39 |   <div slot="center">Title</div>
40 |   <button slot="end">Close</button>
41 | </ds-modal-header>
42 | ```
43 | 
44 | Content is rendered into:
45 | 
46 | - `[slot=start]` → left section
47 | - `[slot=center]` → center section
48 | - `ds-modal-header-drag`, `[modal-header-image]` → center below title
49 | - `[slot=end]` → right section
50 | 
51 | ### `ds-modal-content`
52 | 
53 | Projects content as modal body:
54 | 
55 | ```html
56 | <ds-modal-content> Modal text goes here. </ds-modal-content>
57 | ```
58 | 
59 | ---
60 | 
61 | ## Host Element Behavior
62 | 
63 | ### `ds-modal`
64 | 
65 | - Host class: `ds-modal`
66 | - Attributes:
67 | - `role="dialog"`
68 | - `aria-label="Modal dialog"`
69 | 
70 | ### `ds-modal-header`
71 | 
72 | - Host class: `ds-modal-header`
73 | - Dynamic class based on `variant`: `ds-modal-header-surface`, `ds-modal-header-surface-low`, etc.
74 | - Attributes:
75 | - `role="dialog"`
76 | - `aria-label="Modal header dialog"`
77 | 
78 | ### `ds-modal-header-drag`
79 | 
80 | - Host class: `ds-modal-header-drag`
81 | - Attributes:
82 | - `role="dialog"`
83 | - `aria-label="Modal header drag dialog"`
84 | 
85 | ### `ds-modal-content`
86 | 
87 | - Host class: `ds-modal-content`
88 | - No interactive attributes applied
89 | 
```

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

```typescript
 1 | import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
 2 | 
 3 | @Component({
 4 |   selector: 'app-mixed-styles',
 5 |   standalone: true,
 6 |   schemas: [CUSTOM_ELEMENTS_SCHEMA],
 7 |   template: `
 8 |     <!-- ✅ Good: Using DSButton -->
 9 |     <ds-button>Good Button</ds-button>
10 | 
11 |     <!-- ❌ Bad: Legacy button class -->
12 |     <button class="btn btn-primary">Bad Button</button>
13 | 
14 |     <!-- ✅ Good: Using DSModal -->
15 |     <ds-modal [open]="true">
16 |       <p>Good Modal Content</p>
17 |     </ds-modal>
18 | 
19 |     <!-- ❌ Bad: Custom modal with legacy styles -->
20 |     <div class="modal">
21 |       <div class="modal-content">
22 |         <h2>Bad Modal</h2>
23 |         <p>This is a legacy modal.</p>
24 |       </div>
25 |     </div>
26 | 
27 |     <!-- ✅ Good: DSProgressBar -->
28 |     <ds-progress-bar [value]="50"></ds-progress-bar>
29 | 
30 |     <!-- ❌ Bad: Manually styled progress bar -->
31 |     <div class="progress-bar">
32 |       <div class="progress" style="width: 50%;"></div>
33 |     </div>
34 | 
35 |     <!-- ✅ Good: DSDropdown -->
36 |     <ds-dropdown [options]="['Option 1', 'Option 2']"></ds-dropdown>
37 | 
38 |     <!-- ❌ Bad: Legacy dropdown -->
39 |     <select class="dropdown">
40 |       <option>Option 1</option>
41 |       <option>Option 2</option>
42 |     </select>
43 | 
44 |     <!-- ✅ Good: Using DSAlert -->
45 |     <ds-alert type="error"> Good Alert </ds-alert>
46 | 
47 |     <!-- ❌ Bad: Manually styled alert -->
48 |     <div class="alert alert-danger">Bad Alert</div>
49 | 
50 |     <!-- ✅ Good: Using DSTooltip -->
51 |     <ds-tooltip content="Good tooltip">Hover me</ds-tooltip>
52 | 
53 |     <!-- ❌ Bad: Legacy tooltip -->
54 |     <div class="tooltip">Bad tooltip</div>
55 | 
56 |     <!-- ✅ Good: Using DSBreadcrumb -->
57 |     <ds-breadcrumb>
58 |       <ds-breadcrumb-item>Home</ds-breadcrumb-item>
59 |       <ds-breadcrumb-item>Products</ds-breadcrumb-item>
60 |       <ds-breadcrumb-item>Details</ds-breadcrumb-item>
61 |     </ds-breadcrumb>
62 | 
63 |     <!-- ❌ Bad: Manually created breadcrumb -->
64 |     <nav class="breadcrumb">
65 |       <span>Home</span> / <span>Products</span> / <span>Details</span>
66 |     </nav>
67 |   `,
68 | })
69 | export class MixedStylesComponent3 {}
70 | 
```

--------------------------------------------------------------------------------
/packages/shared/angular-ast-utils/ai/EXAMPLES.md:
--------------------------------------------------------------------------------

```markdown
 1 | # Examples
 2 | 
 3 | ## 1 — Parsing components
 4 | 
 5 | > Parse a single Angular component file and list component class names.
 6 | 
 7 | ```ts
 8 | import { parseComponents } from 'angular-ast-utils';
 9 | 
10 | const comps = await parseComponents(['src/app/app.component.ts']);
11 | console.log(comps.map((c) => c.className));
12 | ```
13 | 
14 | ---
15 | 
16 | ## 2 — Checking for a CSS class
17 | 
18 | > Detect whether a given class name appears in an Angular `[ngClass]` binding.
19 | 
20 | ```ts
21 | import { ngClassesIncludeClassName } from 'angular-ast-utils';
22 | 
23 | const source = "{'btn' : isActive}";
24 | const hasBtn = ngClassesIncludeClassName(source, 'btn');
25 | console.log(hasBtn); // → true
26 | ```
27 | 
28 | ---
29 | 
30 | ## 3 — Finding Angular units by type
31 | 
32 | > Find all components, directives, pipes, or services in a directory.
33 | 
34 | ```ts
35 | import { findAngularUnits } from 'angular-ast-utils';
36 | 
37 | const componentFiles = await findAngularUnits('./src/app', 'component');
38 | const serviceFiles = await findAngularUnits('./src/app', 'service');
39 | console.log(componentFiles); // → ['./src/app/app.component.ts', ...]
40 | ```
41 | 
42 | ---
43 | 
44 | ## 4 — Parsing Angular units in a directory
45 | 
46 | > Parse all Angular components in a directory and get their metadata.
47 | 
48 | ```ts
49 | import { parseAngularUnit } from 'angular-ast-utils';
50 | 
51 | const components = await parseAngularUnit('./src/app', 'component');
52 | console.log(components.map((c) => c.className)); // → ['AppComponent', ...]
53 | ```
54 | 
55 | ---
56 | 
57 | ## 5 — Visiting component templates
58 | 
59 | > Run a visitor function against a component's template AST.
60 | 
61 | ```ts
62 | import { visitComponentTemplate } from 'angular-ast-utils';
63 | 
64 | await visitComponentTemplate(component, searchTerm, async (term, template) => {
65 |   // Process template AST and return issues
66 |   return [];
67 | });
68 | ```
69 | 
70 | ---
71 | 
72 | ## 6 — Visiting component styles
73 | 
74 | > Run a visitor function against a component's styles.
75 | 
76 | ```ts
77 | import { visitComponentStyles } from 'angular-ast-utils';
78 | 
79 | const issues = await visitComponentStyles(
80 |   component,
81 |   searchTerm,
82 |   async (term, style) => {
83 |     // Process style AST and return issues
84 |     return [];
85 |   }
86 | );
87 | ```
88 | 
```

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

```typescript
 1 | import { describe, expect } from 'vitest';
 2 | import { parseStylesheet } from './stylesheet.parse';
 3 | import { Rule } from 'postcss';
 4 | import { styleAstRuleToSource } from './utils';
 5 | 
 6 | describe('styleAstRuleToSource', () => {
 7 |   it('should have line number starting from 1', () => {
 8 |     const result = parseStylesheet(`.btn{ color: red; }`, 'inline-styles').root;
 9 |     const source = styleAstRuleToSource(result?.nodes?.at(0) as Rule);
10 |     expect(source).toStrictEqual({
11 |       file: expect.stringMatching(/inline-styles$/),
12 |       position: {
13 |         startLine: 1,
14 |         startColumn: 1,
15 |         endLine: 1,
16 |         endColumn: 19,
17 |       },
18 |     });
19 |   });
20 | 
21 |   it('should have line number where startLine is respected', () => {
22 |     const result = parseStylesheet(`.btn{ color: red; }`, 'styles.css').root;
23 |     const source = styleAstRuleToSource(result?.nodes?.at(0) as Rule, 4);
24 |     expect(source).toStrictEqual({
25 |       file: expect.stringMatching(/styles\.css$/),
26 |       position: {
27 |         startLine: 5,
28 |         startColumn: 1,
29 |         endLine: 5,
30 |         endColumn: 19,
31 |       },
32 |     });
33 |   });
34 | 
35 |   it('should have correct line number for starting line breaks', () => {
36 |     const result = parseStylesheet(
37 |       `
38 | 
39 | .btn{ color: red; }`,
40 |       'styles.css',
41 |     ).root;
42 |     const source = styleAstRuleToSource(result?.nodes?.at(0) as Rule);
43 |     expect(source).toStrictEqual({
44 |       file: expect.stringMatching(/styles\.css$/),
45 |       position: {
46 |         startLine: 3,
47 |         startColumn: 1,
48 |         endLine: 3,
49 |         endColumn: 19,
50 |       },
51 |     });
52 |   });
53 | 
54 |   it('should have correct line number for spans', () => {
55 |     const result = parseStylesheet(
56 |       `
57 | .btn{
58 |   color: red;
59 | }`,
60 |       'styles.css',
61 |     ).root;
62 | 
63 |     const source = styleAstRuleToSource(result?.nodes?.at(0) as Rule);
64 |     expect(source).toStrictEqual({
65 |       file: expect.stringMatching(/styles\.css$/),
66 |       position: {
67 |         startLine: 2,
68 |         startColumn: 1,
69 |         endLine: 4,
70 |         endColumn: 1,
71 |       },
72 |     });
73 |   });
74 | });
75 | 
```

--------------------------------------------------------------------------------
/packages/angular-mcp/package.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "name": "@push-based/angular-toolkit-mcp",
 3 |   "version": "0.2.0",
 4 |   "description": "A Model Context Protocol server for Angular project analysis and refactoring",
 5 |   "keywords": [
 6 |     "mcp",
 7 |     "angular",
 8 |     "refactoring",
 9 |     "analysis",
10 |     "model-context-protocol"
11 |   ],
12 |   "author": "Push-Based",
13 |   "license": "MIT",
14 |   "repository": {
15 |     "type": "git",
16 |     "url": "https://github.com/push-based/angular-toolkit-mcp.git"
17 |   },
18 |   "homepage": "https://github.com/push-based/angular-toolkit-mcp",
19 |   "bugs": "https://github.com/push-based/angular-toolkit-mcp/issues",
20 |   "engines": {
21 |     "node": ">=18"
22 |   },
23 |   "publishConfig": {
24 |     "access": "public"
25 |   },
26 |   "bin": {
27 |     "angular-toolkit-mcp": "main.js"
28 |   },
29 |   "files": [
30 |     "main.js",
31 |     "*.js",
32 |     "README.md"
33 |   ],
34 |   "nx": {
35 |     "implicitDependencies": [
36 |       "angular-mcp-server"
37 |     ],
38 |     "targets": {
39 |       "serve": {
40 |         "executor": "@nx/js:node",
41 |         "defaultConfiguration": "development",
42 |         "dependsOn": [
43 |           "build"
44 |         ],
45 |         "options": {
46 |           "buildTarget": "angular-mcp:build",
47 |           "runBuildTargetDependencies": false
48 |         },
49 |         "configurations": {
50 |           "development": {
51 |             "buildTarget": "angular-mcp:build:development"
52 |           },
53 |           "production": {
54 |             "buildTarget": "angular-mcp:build:production"
55 |           }
56 |         }
57 |       },
58 |       "serve-static": {
59 |         "dependsOn": [
60 |           "^build"
61 |         ],
62 |         "command": "node packages/angular-mcp/dist/main.js"
63 |       },
64 |       "debug": {
65 |         "dependsOn": [
66 |           "build"
67 |         ],
68 |         "command": "npx @modelcontextprotocol/inspector node packages/angular-mcp/dist/main.js --workspaceRoot=/root/path/to/workspace   --ds.uiRoot=packages/minimal-repo/packages/design-system/ui --ds.storybookDocsRoot=packages/minimal-repo/packages/design-system/storybook-host-app/src/components --ds.deprecatedCssClassesPath=packages/minimal-repo/packages/design-system/component-options.mjs"
69 |       }
70 |     }
71 |   }
72 | }
73 | 
```

--------------------------------------------------------------------------------
/packages/minimal-repo/packages/design-system/ui/segmented-control/src/segmented-control.component.html:
--------------------------------------------------------------------------------

```html
 1 | <div class="ds-segmented-control-container" #scContainer>
 2 |   <div
 3 |     class="ds-segmented-controls"
 4 |     [attr.role]="roleType()"
 5 |     [class.ds-sc-ready]="isReady()"
 6 |     [class.ds-segment-full-width]="fullWidth()"
 7 |     [class.ds-segment-inverse]="inverse()"
 8 |     (keydown)="onKeydown($event)"
 9 |   >
10 |     @for (option of segmentedOptions(); track option.name()) {
11 |       <div
12 |         #tabOption
13 |         class="ds-segment-item"
14 |         [class.ds-segment-selected]="
15 |           option.name() === this.selectedOption()?.name()
16 |         "
17 |         [id]="'ds-segment-item-' + option.name()"
18 |         [attr.tabindex]="
19 |           option.name() === this.selectedOption()?.name() ? 0 : -1
20 |         "
21 |         [attr.role]="roleType() === 'tablist' ? 'tab' : 'radio'"
22 |         [attr.aria-selected]="
23 |           roleType() === 'tablist'
24 |             ? option.name() === this.selectedOption()?.name()
25 |               ? 'true'
26 |               : 'false'
27 |             : null
28 |         "
29 |         [attr.aria-checked]="
30 |           roleType() === 'radiogroup'
31 |             ? option.name() === this.selectedOption()?.name()
32 |               ? 'true'
33 |               : 'false'
34 |             : null
35 |         "
36 |         [attr.aria-label]="option.title() || option.name()"
37 |         (click)="selectOption(option.name(), $event)"
38 |       >
39 |         <input
40 |           type="radio"
41 |           class="ds-segmented-control-hidden-input"
42 |           [value]="option.name()"
43 |           [name]="option.name()"
44 |           [id]="'ds-sc-option-' + option.name()"
45 |           [checked]="option.selected()"
46 |           [attr.aria-labelledby]="'ds-segment-item-' + option.name()"
47 |           [title]="option.title()"
48 |         />
49 |         <label
50 |           class="ds-segment-item-label"
51 |           [for]="'ds-sc-option-' + option.title()"
52 |           [class.ds-segmented-item-two-line-text]="twoLineTruncation()"
53 |           [class.ds-segment-item-custom-template]="option.customTemplate()"
54 |         >
55 |           @if (option.customTemplate()) {
56 |             <ng-container [ngTemplateOutlet]="option.customTemplate()!" />
57 |           } @else {
58 |             {{ option.title() }}
59 |           }
60 |         </label>
61 |       </div>
62 |     }
63 |   </div>
64 | </div>
65 | 
```

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

```typescript
 1 | /* eslint-disable prefer-const */
 2 | import { describe, it, expect } from 'vitest';
 3 | 
 4 | import {
 5 |   createDomPathDictionary,
 6 |   isDomPath,
 7 |   isValidDomPath,
 8 |   addDomPath,
 9 |   processDomPaths,
10 | } from '../utils/dom-path-utils.js';
11 | 
12 | const SAMPLE_PATH = 'div#root > span.foo > button.bar';
13 | 
14 | describe('dom-path-utils', () => {
15 |   describe('isDomPath / isValidDomPath', () => {
16 |     it('detects DOM-like selector strings correctly', () => {
17 |       expect(isDomPath(SAMPLE_PATH)).toBe(true);
18 |       expect(isValidDomPath(SAMPLE_PATH)).toBe(true);
19 | 
20 |       expect(isDomPath('div')).toBe(false);
21 |       expect(isDomPath('div.foo')).toBe(false);
22 |       const mediaPath = `${SAMPLE_PATH} @media`;
23 |       expect(isDomPath(mediaPath)).toBe(true);
24 |       expect(isValidDomPath(mediaPath)).toBe(false);
25 |     });
26 |   });
27 | 
28 |   describe('addDomPath & createDomPathDictionary', () => {
29 |     it('adds new paths and deduplicates existing ones', () => {
30 |       const dict = createDomPathDictionary();
31 | 
32 |       const ref1 = addDomPath(dict, SAMPLE_PATH);
33 |       expect(ref1).toEqual({ $domPath: 0 });
34 |       expect(dict.paths[0]).toBe(SAMPLE_PATH);
35 |       expect(dict.stats.totalPaths).toBe(1);
36 |       expect(dict.stats.uniquePaths).toBe(1);
37 |       expect(dict.stats.duplicateReferences).toBe(0);
38 | 
39 |       const ref2 = addDomPath(dict, SAMPLE_PATH);
40 |       expect(ref2).toEqual({ $domPath: 0 });
41 |       expect(dict.stats.totalPaths).toBe(2);
42 |       expect(dict.stats.uniquePaths).toBe(1);
43 |       expect(dict.stats.duplicateReferences).toBe(1);
44 |     });
45 |   });
46 | 
47 |   describe('processDomPaths', () => {
48 |     it('recursively replaces DOM path strings with references', () => {
49 |       const dict = createDomPathDictionary();
50 | 
51 |       const input = {
52 |         pathA: SAMPLE_PATH,
53 |         nested: ['no-dom-path', SAMPLE_PATH, { deeper: SAMPLE_PATH }],
54 |       };
55 | 
56 |       const processed = processDomPaths(input, dict);
57 | 
58 |       expect(processed.pathA).toEqual({ $domPath: 0 });
59 |       expect(processed.nested[1]).toEqual({ $domPath: 0 });
60 |       expect(processed.nested[2].deeper).toEqual({ $domPath: 0 });
61 | 
62 |       expect(dict.paths).toEqual([SAMPLE_PATH]);
63 |       expect(dict.stats.uniquePaths).toBe(1);
64 |     });
65 |   });
66 | });
67 | 
```

--------------------------------------------------------------------------------
/packages/minimal-repo/packages/application/src/app/app.component.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { Component } from '@angular/core';
 2 | import { RouterOutlet } from '@angular/router';
 3 | import { BadAlertComponent } from './components/refactoring-tests/bad-alert.component';
 4 | import { BadAlertTooltipInputComponent } from './components/refactoring-tests/bad-alert-tooltip-input.component';
 5 | import { BadButtonDropdownComponent } from './components/refactoring-tests/bad-button-dropdown.component';
 6 | import { MixedStylesComponent } from './components/refactoring-tests/bad-mixed.component';
 7 | import { BadModalProgressComponent } from './components/refactoring-tests/bad-modal-progress.component';
 8 | import { BadMixedExternalAssetsComponent } from './components/refactoring-tests/bad-mixed-external-assets.component';
 9 | import { BadDocumentComponent } from './components/refactoring-tests/bad-document.component';
10 | import { BadWindowComponent } from './components/refactoring-tests/bad-window.component';
11 | import { BadThisWindowDocumentComponent } from './components/refactoring-tests/bad-this-window-document.component';
12 | import { BadGlobalThisComponent } from './components/refactoring-tests/bad-global-this.component';
13 | 
14 | @Component({
15 |   selector: 'app-root',
16 |   imports: [
17 |     RouterOutlet,
18 |     BadAlertComponent,
19 |     BadAlertTooltipInputComponent,
20 |     BadModalProgressComponent,
21 |     BadButtonDropdownComponent,
22 |     MixedStylesComponent,
23 |     BadMixedExternalAssetsComponent,
24 |     BadDocumentComponent,
25 |     BadGlobalThisComponent,
26 |     BadWindowComponent,
27 |     BadThisWindowDocumentComponent,
28 |   ],
29 |   template: `
30 |     <h1>{{ title }}</h1>
31 |     <button class="btn">Sports</button>
32 |     <app-bad-alert></app-bad-alert>
33 |     <app-bad-alert-tooltip-input></app-bad-alert-tooltip-input>
34 |     <app-bad-modal-progress></app-bad-modal-progress>
35 |     <app-mixed-styles></app-mixed-styles>
36 |     <app-bad-button-dropdown></app-bad-button-dropdown>
37 |     <app-bad-mixed-external-assets></app-bad-mixed-external-assets>
38 |     <app-bad-window></app-bad-window>
39 |     <app-bad-this-window-document></app-bad-this-window-document>
40 |     <app-bad-document></app-bad-document>
41 |     <app-bad-global-this></app-bad-global-this>
42 |     <router-outlet />
43 |   `,
44 | })
45 | export class AppComponent {
46 |   title = 'minimal';
47 | }
48 | 
```

--------------------------------------------------------------------------------
/packages/shared/angular-ast-utils/src/lib/template/noop-tmpl-visitor.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import type {
 2 |   TmplAstVisitor,
 3 |   TmplAstElement,
 4 |   TmplAstTemplate,
 5 |   TmplAstContent,
 6 |   TmplAstText,
 7 |   TmplAstBoundText,
 8 |   TmplAstIcu,
 9 |   TmplAstReference,
10 |   TmplAstVariable,
11 |   TmplAstBoundEvent,
12 |   TmplAstBoundAttribute,
13 |   TmplAstTextAttribute,
14 |   TmplAstUnknownBlock,
15 |   TmplAstDeferredBlock,
16 |   TmplAstDeferredBlockError,
17 |   TmplAstDeferredBlockLoading,
18 |   TmplAstDeferredBlockPlaceholder,
19 |   TmplAstDeferredTrigger,
20 |   TmplAstIfBlock,
21 |   TmplAstIfBlockBranch,
22 |   TmplAstSwitchBlock,
23 |   TmplAstSwitchBlockCase,
24 |   TmplAstForLoopBlock,
25 |   TmplAstForLoopBlockEmpty,
26 |   TmplAstLetDeclaration,
27 | } from '@angular/compiler' with { 'resolution-mode': 'import' };
28 | 
29 | /**
30 |  * Base visitor that does nothing.
31 |  * Extend this in concrete visitors so you only override what you need.
32 |  */
33 | export abstract class NoopTmplVisitor implements TmplAstVisitor<void> {
34 |   /* eslint-disable @typescript-eslint/no-empty-function */
35 |   visitElement(_: TmplAstElement): void {}
36 |   visitTemplate(_: TmplAstTemplate): void {}
37 |   visitContent(_: TmplAstContent): void {}
38 |   visitText(_: TmplAstText): void {}
39 |   visitBoundText(_: TmplAstBoundText): void {}
40 |   visitIcu(_: TmplAstIcu): void {}
41 |   visitReference(_: TmplAstReference): void {}
42 |   visitVariable(_: TmplAstVariable): void {}
43 |   visitBoundEvent(_: TmplAstBoundEvent): void {}
44 |   visitBoundAttribute(_: TmplAstBoundAttribute): void {}
45 |   visitTextAttribute(_: TmplAstTextAttribute): void {}
46 |   visitUnknownBlock(_: TmplAstUnknownBlock): void {}
47 |   visitDeferredBlock(_: TmplAstDeferredBlock): void {}
48 |   visitDeferredBlockError(_: TmplAstDeferredBlockError): void {}
49 |   visitDeferredBlockLoading(_: TmplAstDeferredBlockLoading): void {}
50 |   visitDeferredBlockPlaceholder(_: TmplAstDeferredBlockPlaceholder): void {}
51 |   visitDeferredTrigger(_: TmplAstDeferredTrigger): void {}
52 |   visitIfBlock(_: TmplAstIfBlock): void {}
53 |   visitIfBlockBranch(_: TmplAstIfBlockBranch): void {}
54 |   visitSwitchBlock(_: TmplAstSwitchBlock): void {}
55 |   visitSwitchBlockCase(_: TmplAstSwitchBlockCase): void {}
56 |   visitForLoopBlock(_: TmplAstForLoopBlock): void {}
57 |   visitForLoopBlockEmpty(_: TmplAstForLoopBlockEmpty): void {}
58 |   visitLetDeclaration(_: TmplAstLetDeclaration): void {}
59 |   /* eslint-enable */
60 | }
61 | 
```

--------------------------------------------------------------------------------
/docs/getting-started.md:
--------------------------------------------------------------------------------

```markdown
 1 | # Getting Started with Angular MCP Toolkit
 2 | 
 3 | A concise, hands-on guide to install, configure, and verify the Angular MCP server in under **5 minutes**.
 4 | 
 5 | ---
 6 | 
 7 | ## 1. Prerequisites
 8 | 
 9 | | Tool | Minimum Version | Notes |
10 | | ---- | --------------- | ----- |
11 | | Node.js | **18.x** LTS | Tested with 18.18 ⬆︎ |
12 | | npm | **9.x** | Bundled with Node LTS |
13 | | Nx CLI | **≥ 21** | `npm i -g nx` |
14 | | Git | Any recent | For workspace cloning |
15 | 
16 | > The server itself is framework-agnostic, but most built-in tools assume an **Nx workspace** with Angular projects.
17 | 
18 | ---
19 | 
20 | ## 2. Install the Server
21 | 
22 | ### Clone the repository
23 | 
24 | ```bash
25 | git clone https://github.com/push-based/angular-toolkit-mcp.git
26 | cd angular-toolkit-mcp
27 | npm install   # install workspace dependencies
28 | ```
29 | 
30 | The MCP server source resides under `packages/angular-mcp/` and `packages/angular-mcp-server/`. No package needs to be fetched from the npm registry.
31 | 
32 | ---
33 | 
34 | ## 3. Register with Your Editor
35 | 
36 | Instead of the palette-based flow, copy the manual configuration from your workspace’s `.cursor/mcp.json` (shown below) and adjust paths if necessary.
37 | 
38 | ```json
39 | {
40 |   "mcpServers": {
41 |     "angular-mcp": {
42 |       "command": "node",
43 |       "args": [
44 |         "./packages/angular-mcp/dist/main.js",
45 |         "--workspaceRoot=/absolute/path/to/angular-toolkit-mcp",
46 |         "--ds.storybookDocsRoot=packages/minimal-repo/packages/design-system/storybook-host-app/src/components",
47 |         "--ds.deprecatedCssClassesPath=packages/minimal-repo/packages/design-system/component-options.mjs",
48 |         "--ds.uiRoot=packages/minimal-repo/packages/design-system/ui"
49 |       ]
50 |     }
51 |   }
52 | }
53 | ```
54 | 
55 | Add or edit this JSON in **Cursor → Settings → MCP Servers** (or the equivalent dialog in your editor).
56 | 
57 | ---
58 | 
59 | ## 4. Next Steps
60 | 
61 | 🔗 Continue with the [Architecture & Internal Design](./architecture-internal-design.md) document (work-in-progress).
62 | 
63 | 🚀 Jump straight into [Writing Custom Tools](./writing-custom-tools.md) when ready.
64 | 
65 | ---
66 | 
67 | ## Troubleshooting
68 | 
69 | | Symptom | Possible Cause | Fix |
70 | | ------- | -------------- | --- |
71 | | `command not found: nx` | Nx CLI missing | `npm i -g nx` |
72 | | Editor shows “tool not found” | Server not running or wrong path in `mcp.json` | Check configuration and restart editor |
73 | 
74 | ---
75 | 
76 | *Happy coding!* ✨ 
```

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

```typescript
 1 | import type { DomPathDictionary } from '../../shared/models/types.js';
 2 | 
 3 | /**
 4 |  * Creates a new DOM path dictionary
 5 |  */
 6 | export function createDomPathDictionary(): DomPathDictionary {
 7 |   return {
 8 |     paths: [],
 9 |     lookup: new Map<string, number>(),
10 |     stats: {
11 |       totalPaths: 0,
12 |       uniquePaths: 0,
13 |       duplicateReferences: 0,
14 |       bytesBeforeDeduplication: 0,
15 |       bytesAfterDeduplication: 0,
16 |     },
17 |   };
18 | }
19 | 
20 | /**
21 |  * Detects if a string is a DOM path based on Angular component patterns
22 |  */
23 | export function isDomPath(str: string): boolean {
24 |   return (
25 |     typeof str === 'string' &&
26 |     str.length > 20 &&
27 |     str.includes(' > ') &&
28 |     (str.includes('.') || str.includes('#')) &&
29 |     /^[a-zA-Z]/.test(str)
30 |   );
31 | }
32 | 
33 | /**
34 |  * Validates that a string is specifically a DOM path and not just any CSS selector
35 |  */
36 | export function isValidDomPath(str: string): boolean {
37 |   return (
38 |     isDomPath(str) &&
39 |     !str.includes('@media') &&
40 |     !str.includes('{') &&
41 |     !str.includes('}') &&
42 |     str.split(' > ').length > 2
43 |   );
44 | }
45 | 
46 | /**
47 |  * Adds a DOM path to the dictionary and returns its reference
48 |  */
49 | export function addDomPath(
50 |   dict: DomPathDictionary,
51 |   path: string,
52 | ): { $domPath: number } {
53 |   dict.stats.totalPaths++;
54 |   dict.stats.bytesBeforeDeduplication += path.length;
55 | 
56 |   if (dict.lookup.has(path)) {
57 |     dict.stats.duplicateReferences++;
58 |     dict.stats.bytesAfterDeduplication += 12;
59 |     return { $domPath: dict.lookup.get(path)! };
60 |   }
61 | 
62 |   const index = dict.paths.length;
63 |   dict.paths.push(path);
64 |   dict.lookup.set(path, index);
65 |   dict.stats.uniquePaths++;
66 |   dict.stats.bytesAfterDeduplication += 12;
67 | 
68 |   return { $domPath: index };
69 | }
70 | 
71 | /**
72 |  * Processes a value and replaces DOM paths with references
73 |  */
74 | export function processDomPaths(value: any, dict: DomPathDictionary): any {
75 |   if (typeof value === 'string') {
76 |     if (isValidDomPath(value)) {
77 |       return addDomPath(dict, value);
78 |     }
79 |     return value;
80 |   }
81 | 
82 |   if (Array.isArray(value)) {
83 |     return value.map((item) => processDomPaths(item, dict));
84 |   }
85 | 
86 |   if (value && typeof value === 'object') {
87 |     const processed: any = {};
88 |     for (const [key, val] of Object.entries(value)) {
89 |       processed[key] = processDomPaths(val, dict);
90 |     }
91 |     return processed;
92 |   }
93 | 
94 |   return value;
95 | }
96 | 
```

--------------------------------------------------------------------------------
/packages/minimal-repo/packages/design-system/ui/badge/src/badge.component.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import {
 2 |   ChangeDetectionStrategy,
 3 |   Component,
 4 |   ElementRef,
 5 |   ViewEncapsulation,
 6 |   booleanAttribute,
 7 |   computed,
 8 |   input,
 9 | } from '@angular/core';
10 | 
11 | export const DS_BADGE_VARIANT_ARRAY = [
12 |   'primary',
13 |   'primary-strong',
14 |   'primary-subtle',
15 |   'secondary',
16 |   'secondary-strong',
17 |   'secondary-subtle',
18 |   'green',
19 |   'green-strong',
20 |   'green-subtle',
21 |   'blue',
22 |   'blue-strong',
23 |   'blue-subtle',
24 |   'red',
25 |   'red-strong',
26 |   'red-subtle',
27 |   'purple',
28 |   'purple-strong',
29 |   'purple-subtle',
30 |   'neutral',
31 |   'neutral-strong',
32 |   'neutral-subtle',
33 |   'yellow',
34 |   'yellow-strong',
35 |   'yellow-subtle',
36 |   'orange',
37 |   'orange-strong',
38 |   'orange-subtle',
39 | ] as const;
40 | 
41 | export type DsBadgeVariant = (typeof DS_BADGE_VARIANT_ARRAY)[number];
42 | 
43 | export const DS_BADGE_SIZE_ARRAY = ['xsmall', 'medium'] as const;
44 | export type DsBadgeSize = (typeof DS_BADGE_SIZE_ARRAY)[number];
45 | 
46 | @Component({
47 |   selector: 'ds-badge',
48 |   template: `
49 |     <div class="ds-badge-slot-container">
50 |       <ng-content select="[slot=start]" />
51 |     </div>
52 |     <span class="ds-badge-text">
53 |       <ng-content />
54 |     </span>
55 |     <div class="ds-badge-slot-container">
56 |       <ng-content select="[slot=end]" />
57 |     </div>
58 |   `,
59 |   host: {
60 |     '[class]': 'hostClass()',
61 |     '[class.ds-badge-disabled]': 'disabled()',
62 |     '[class.ds-badge-inverse]': 'inverse()',
63 |     '[attr.aria-label]': 'getAriaLabel()',
64 |     role: 'img', // for now we are using role img till we find better solution to work with nvda
65 |   },
66 |   standalone: true,
67 |   encapsulation: ViewEncapsulation.None,
68 |   changeDetection: ChangeDetectionStrategy.OnPush,
69 | })
70 | export class DsBadge {
71 |   size = input<DsBadgeSize>('medium');
72 |   variant = input<DsBadgeVariant>('primary');
73 |   disabled = input(false, { transform: booleanAttribute });
74 |   inverse = input(false, { transform: booleanAttribute });
75 | 
76 |   hostClass = computed(
77 |     () => `ds-badge ds-badge-${this.size()} ds-badge-${this.variant()}`,
78 |   );
79 | 
80 |   constructor(public elementRef: ElementRef<HTMLElement>) {}
81 | 
82 |   public getAriaLabel(): string {
83 |     const mainContent = this.elementRef.nativeElement
84 |       .querySelector('.ds-badge-text')
85 |       ?.textContent?.trim();
86 | 
87 |     const label = mainContent || '';
88 | 
89 |     if (this.disabled()) {
90 |       return `Disabled badge: ${label}`;
91 |     }
92 |     return `Badge: ${label}`;
93 |   }
94 | }
95 | 
```

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

```markdown
 1 | # Public API — Quick Reference
 2 | 
 3 | | Symbol                                 | Kind     | Signature                                                                                     | Summary                                              |
 4 | | -------------------------------------- | -------- | --------------------------------------------------------------------------------------------- | ---------------------------------------------------- |
 5 | | `ANGULAR_DS_USAGE_PLUGIN_SLUG`         | constant | `const ANGULAR_DS_USAGE_PLUGIN_SLUG: string`                                                  | Plugin slug identifier for ds-component-coverage     |
 6 | | `ComponentCoverageRunnerOptionsSchema` | schema   | `const ComponentCoverageRunnerOptionsSchema: ZodObject`                                       | Zod schema for runner configuration validation       |
 7 | | `ComponentReplacement`                 | type     | `type ComponentReplacement`                                                                   | Type for component replacement configuration         |
 8 | | `ComponentReplacementSchema`           | schema   | `const ComponentReplacementSchema: ZodObject`                                                 | Zod schema for component replacement validation      |
 9 | | `CreateRunnerConfig`                   | type     | `type CreateRunnerConfig`                                                                     | Type alias for runner configuration                  |
10 | | `dsComponentCoveragePlugin`            | function | `dsComponentCoveragePlugin(options: DsComponentUsagePluginConfig): PluginConfig`              | Create DS component coverage plugin for Code Pushup  |
11 | | `DsComponentUsagePluginConfig`         | type     | `type DsComponentUsagePluginConfig`                                                           | Configuration type for the DS component usage plugin |
12 | | `getAngularDsUsageCategoryRefs`        | function | `getAngularDsUsageCategoryRefs(componentReplacements: ComponentReplacement[]): CategoryRef[]` | Generate category references for audit organization  |
13 | | `runnerFunction`                       | function | `runnerFunction(config: CreateRunnerConfig): Promise<AuditOutputs>`                           | Execute DS component coverage analysis               |
14 | 
```

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

```markdown
 1 | ## Inputs
 2 | 
 3 | | Name       | Type                                                                   | Default     | Description                                            |
 4 | | ---------- | ---------------------------------------------------------------------- | ----------- | ------------------------------------------------------ |
 5 | | `size`     | `'xsmall'` \| `'medium'`                                               | `'medium'`  | Controls the badge size.                               |
 6 | | `variant`  | A value from `DsBadgeVariant`<br/>(e.g. `'primary'`, `'green-strong'`) | `'primary'` | Visual style variant (color + intensity).              |
 7 | | `disabled` | `boolean`                                                              | `false`     | Visually and semantically marks the badge as disabled. |
 8 | | `inverse`  | `boolean`                                                              | `false`     | Applies inverse theme styling (for dark backgrounds).  |
 9 | 
10 | ---
11 | 
12 | ## Outputs / Events
13 | 
14 | This component does not emit any custom events.
15 | 
16 | ---
17 | 
18 | ## Content Projection
19 | 
20 | The badge supports slot-based content for flexible icon/text layouts.
21 | 
22 | ### Named Slots
23 | 
24 | - `[slot=start]` – content rendered before the main text.
25 | - `[slot=end]` – content rendered after the main text.
26 | 
27 | ### Default Slot
28 | 
29 | Text or elements directly inside the component will be rendered in the central label span.
30 | 
31 | ```html
32 | <ds-badge variant="green-strong">
33 |   <span slot="start">✔</span>
34 |   Confirmed
35 |   <span slot="end">✓</span>
36 | </ds-badge>
37 | ```
38 | 
39 | ---
40 | 
41 | ## Host Element Behavior
42 | 
43 | The following CSS classes are dynamically applied to the host element to reflect component state:
44 | 
45 | - `ds-badge` – base class applied to all badge instances
46 | - `ds-badge-xsmall` or `ds-badge-medium` – based on the `size` input
47 | - `ds-badge-[variant]` – where `[variant]` corresponds to the selected variant (e.g. `ds-badge-green-subtle`)
48 | - `ds-badge-inverse` – applied when `inverse` is `true`
49 | - `ds-badge-disabled` – applied when `disabled` is `true`
50 | 
51 | These classes are computed and set via the `hostClass()` method using Angular’s `@computed()` signal.
52 | 
53 | The host also defines the following attributes:
54 | 
55 | - `role="img"` – applied for accessibility support
56 | - `aria-label` – dynamically generated from content, prepended with `"Disabled badge:"` if `disabled` is `true`
57 | 
```

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

```typescript
 1 | import * as ts from 'typescript';
 2 | import { removeQuotes } from '@push-based/typescript-ast-utils';
 3 | import { AngularUnit, Asset } from './types.js';
 4 | 
 5 | export function assetFromPropertyValueInitializer<T>({
 6 |   prop,
 7 |   sourceFile,
 8 |   textParser,
 9 | }: {
10 |   prop: ts.PropertyAssignment;
11 |   sourceFile: ts.SourceFile;
12 |   textParser: (text: string) => Promise<T>;
13 | }): Asset<T> {
14 |   const { line: startLine } = sourceFile.getLineAndCharacterOfPosition(
15 |     prop.getStart(sourceFile),
16 |   );
17 |   const value = removeQuotes(prop.initializer, sourceFile);
18 |   return {
19 |     filePath: sourceFile.fileName,
20 |     startLine,
21 |     parse: () => textParser(value),
22 |   } satisfies Asset<T>;
23 | }
24 | 
25 | export function assetFromPropertyArrayInitializer<T>(
26 |   prop: ts.PropertyAssignment,
27 |   sourceFile: ts.SourceFile,
28 |   textParser: (text: string) => Promise<T>,
29 | ): Asset<T>[] {
30 |   const elements: ts.NodeArray<ts.Expression> = ts.isArrayLiteralExpression(
31 |     prop.initializer,
32 |   )
33 |     ? prop.initializer.elements
34 |     : ts.factory.createNodeArray();
35 | 
36 |   return elements.map((element) => {
37 |     const { line: startLine } = sourceFile.getLineAndCharacterOfPosition(
38 |       element.getStart(sourceFile),
39 |     );
40 |     const value = removeQuotes(element, sourceFile);
41 |     return {
42 |       filePath: sourceFile.fileName,
43 |       startLine,
44 |       parse: () => textParser(value),
45 |     } satisfies Asset<T>;
46 |   });
47 | }
48 | 
49 | import { findFilesWithPattern } from '@push-based/utils';
50 | import { parseComponents } from './parse-component.js';
51 | 
52 | const unitToSearchPattern = {
53 |   component: '@Component',
54 |   directive: '@Directive',
55 |   pipe: '@Pipe',
56 |   service: '@Service',
57 | } as const satisfies Record<AngularUnit, string>;
58 | 
59 | export async function findAngularUnits(
60 |   directory: string,
61 |   unit: AngularUnit,
62 | ): Promise<string[]> {
63 |   const searchPattern =
64 |     unitToSearchPattern[unit] ?? unitToSearchPattern.component;
65 |   return await findFilesWithPattern(directory, searchPattern);
66 | }
67 | 
68 | /**
69 |  * Parse Angular units in a given directory.
70 |  *
71 |  * @param directory
72 |  * @param unit
73 |  */
74 | export async function parseAngularUnit(directory: string, unit: AngularUnit) {
75 |   const componentFiles = await findAngularUnits(directory, unit);
76 | 
77 |   switch (unit) {
78 |     case 'component':
79 |       return parseComponents(componentFiles);
80 |     default:
81 |       throw new Error(`Unit ${unit} is not supported for parsing.`);
82 |   }
83 | }
84 | 
```

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

```typescript
 1 | import {
 2 |   createHandler,
 3 |   BaseHandlerOptions,
 4 | } from '../shared/utils/handler-helpers.js';
 5 | import {
 6 |   buildComponentUsageGraph,
 7 |   clearAnalysisCache,
 8 | } from './utils/component-usage-graph-builder.js';
 9 | import { filterGraph, printComponents } from './utils/component-helpers.js';
10 | import { buildComponentUsageGraphSchema } from './models/schema.js';
11 | import { resolveCrossPlatformPath } from '../shared/utils/cross-platform-path.js';
12 | 
13 | interface ComponentUsageGraphOptions extends BaseHandlerOptions {
14 |   directory: string;
15 |   violationFiles: string[];
16 | }
17 | 
18 | export const buildComponentUsageGraphHandler = createHandler<
19 |   ComponentUsageGraphOptions,
20 |   any
21 | >(
22 |   buildComponentUsageGraphSchema.name,
23 |   async (params, { cwd, workspaceRoot }) => {
24 |     const startTime = performance.now();
25 | 
26 |     try {
27 |       const { directory, violationFiles } = params;
28 | 
29 |       if (
30 |         !violationFiles ||
31 |         !Array.isArray(violationFiles) ||
32 |         violationFiles.length === 0
33 |       ) {
34 |         throw new Error(
35 |           'violationFiles parameter is required and must be an array of strings',
36 |         );
37 |       }
38 | 
39 |       const fullComponentUsageGraph = await buildComponentUsageGraph({
40 |         cwd,
41 |         directory,
42 |         workspaceRoot,
43 |       });
44 | 
45 |       const targetPath = resolveCrossPlatformPath(cwd, directory);
46 | 
47 |       const componentUsageGraph =
48 |         violationFiles.length > 0
49 |           ? filterGraph(fullComponentUsageGraph, violationFiles, targetPath)
50 |           : fullComponentUsageGraph;
51 | 
52 |       const content = printComponents(componentUsageGraph, 'entity');
53 |       const totalTime = performance.now() - startTime;
54 | 
55 |       return {
56 |         content,
57 |         timing: `⚡ Analysis completed in ${totalTime.toFixed(2)}ms (${Object.keys(fullComponentUsageGraph).length} files processed)`,
58 |       };
59 |     } finally {
60 |       clearAnalysisCache();
61 |     }
62 |   },
63 |   (result) => {
64 |     // Format the result as text lines
65 |     const lines: string[] = [];
66 | 
67 |     if (Array.isArray(result.content)) {
68 |       result.content.forEach((item: any) => {
69 |         if (item.type === 'text') {
70 |           lines.push(item.text);
71 |         } else {
72 |           lines.push(JSON.stringify(item));
73 |         }
74 |       });
75 |     }
76 | 
77 |     lines.push(result.timing);
78 |     return lines;
79 |   },
80 | );
81 | 
82 | export const buildComponentUsageGraphTools = [
83 |   {
84 |     schema: buildComponentUsageGraphSchema,
85 |     handler: buildComponentUsageGraphHandler,
86 |   },
87 | ];
88 | 
```

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

```typescript
 1 | import { describe, expect } from 'vitest';
 2 | import { tmplAstElementToSource } from './utils';
 3 | import type { TmplAstElement } from '@angular/compiler' with { 'resolution-mode': 'import' };
 4 | 
 5 | describe('tmplAstElementToSource', () => {
 6 |   let parseTemplate: typeof import('@angular/compiler').parseTemplate;
 7 |   beforeAll(async () => {
 8 |     parseTemplate = (await import('@angular/compiler')).parseTemplate;
 9 |   });
10 |   it('should have line number starting from 1', () => {
11 |     const result = parseTemplate(
12 |       `<button class="btn">click</button>`,
13 |       'inline-template.component.ts',
14 |     );
15 |     const attribute = result.nodes.at(0) as TmplAstElement;
16 |     const source = tmplAstElementToSource(attribute);
17 |     expect(source).toStrictEqual({
18 |       file: 'inline-template.component.ts',
19 |       position: {
20 |         startLine: 1,
21 |       },
22 |     });
23 |   });
24 | 
25 |   it('should have line number where startLine is respected', () => {
26 |     const result = parseTemplate(
27 |       `<button class="btn">click</button>`,
28 |       'template.html',
29 |     );
30 |     const attribute = (result.nodes.at(0) as TmplAstElement)?.attributes.at(0);
31 | 
32 |     const source = tmplAstElementToSource(attribute);
33 | 
34 |     expect(source).toStrictEqual({
35 |       file: expect.stringMatching(/template\.html$/),
36 |       position: {
37 |         startLine: 5,
38 |         startColumn: 1,
39 |         endLine: 5,
40 |         endColumn: 19,
41 |       },
42 |     });
43 |   });
44 | 
45 |   it('should have correct line number for starting line breaks', () => {
46 |     const result = parseTemplate(
47 |       `
48 | 
49 | <button class="btn">click</button>`,
50 |       'template.html',
51 |     );
52 |     const attribute = (result.nodes.at(0) as TmplAstElement)?.attributes.at(0);
53 |     const source = tmplAstElementToSource(attribute);
54 | 
55 |     expect(source).toStrictEqual({
56 |       file: expect.stringMatching(/template\.html/),
57 |       position: {
58 |         startLine: 3,
59 |         startColumn: 1,
60 |         endLine: 3,
61 |         endColumn: 19,
62 |       },
63 |     });
64 |   });
65 | 
66 |   it('should have correct line number for spans', () => {
67 |     const result = parseTemplate(
68 |       `<button class="btn">
69 |   click
70 | </button>`,
71 |       'template.html',
72 |     );
73 |     const attribute = result.nodes.at(0)?.attributes.at(0);
74 |     const source = tmplAstElementToSource(attribute);
75 | 
76 |     expect(source).toStrictEqual({
77 |       file: expect.stringMatching(/template\.html$/),
78 |       position: {
79 |         startLine: 1,
80 |         startColumn: 1,
81 |         endLine: 4,
82 |         endColumn: 1,
83 |       },
84 |     });
85 |   });
86 | });
87 | 
```

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

```markdown
 1 | # Utils
 2 | 
 3 | Comprehensive **utility library** providing process execution, file operations, string manipulation, and logging utilities for Node.js applications.
 4 | 
 5 | ## Minimal usage
 6 | 
 7 | ```ts
 8 | import {
 9 |   executeProcess,
10 |   findFilesWithPattern,
11 |   resolveFileCached,
12 |   loadDefaultExport,
13 |   objectToCliArgs,
14 | } from '@push-based/utils';
15 | 
16 | import { slugify } from '@code-pushup/utils';
17 | 
18 | // Execute a process with observer
19 | const result = await executeProcess({
20 |   command: 'node',
21 |   args: ['--version'],
22 |   observer: {
23 |     onStdout: (data) => console.log(data),
24 |   },
25 | });
26 | 
27 | // Find files containing a pattern
28 | const files = await findFilesWithPattern('./src', 'Component');
29 | 
30 | // Resolve file with caching
31 | const content = await resolveFileCached('./config.json');
32 | 
33 | // Load ES module default export
34 | const config = await loadDefaultExport('./config.mjs');
35 | 
36 | // String utilities
37 | const slug = slugify('Hello World!'); // → 'hello-world'
38 | const args = objectToCliArgs({ name: 'test', verbose: true }); // → ['--name="test"', '--verbose']
39 | ```
40 | 
41 | ## Key Features
42 | 
43 | - **Process Execution**: Robust child process management with observers and error handling
44 | - **File Operations**: Cached file resolution and pattern-based file searching
45 | - **ES Module Loading**: Dynamic import of ES modules with default export extraction
46 | - **String Utilities**: Text transformation, slugification, and pluralization
47 | - **CLI Utilities**: Object-to-arguments conversion and command formatting
48 | - **Logging**: Environment-based verbose logging control
49 | - **Type Safety**: Full TypeScript support with comprehensive type definitions
50 | 
51 | ## Use Cases
52 | 
53 | - **Build Tools**: Execute CLI commands with real-time output monitoring
54 | - **File Processing**: Search and resolve files efficiently with caching
55 | - **Module Loading**: Dynamic import of configuration files and plugins
56 | - **Code Generation**: Transform data into CLI arguments and formatted strings
57 | - **Development Tools**: Create development utilities with proper logging
58 | - **Static Analysis**: Find and process files based on content patterns
59 | - **Cross-Platform**: Handle path normalization and command execution
60 | 
61 | ## Documentation map
62 | 
63 | | Doc                            | What you'll find                            |
64 | | ------------------------------ | ------------------------------------------- |
65 | | [FUNCTIONS.md](./FUNCTIONS.md) | A–Z quick reference for every public symbol |
66 | | [EXAMPLES.md](./EXAMPLES.md)   | Runnable scenarios with expected output     |
67 | 
```

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

```markdown
 1 | import { Canvas } from '@storybook/blocks';
 2 | 
 3 | import * as BadgeStories from '../badge.component.stories';
 4 | 
 5 | 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).
 6 | You can make icon movable with properties slot for start and end position.
 7 | 
 8 | <Canvas of={BadgeStories.Default} />
 9 | 
10 | ---
11 | 
12 | ## Usage
13 | 
14 | Import `DsBadge` in your component and apply `ds-badge` selector in your template.
15 | 
16 | ```ts
17 | import { DsBadge } from '@frontend/ui/badge'; // 👈 add to file imports
18 | 
19 | @Component({
20 |   imports: [DsBadge], // 👈 add to component imports
21 |   template: `...`,
22 | })
23 | export class AppComponent {}
24 | ```
25 | 
26 | ---
27 | 
28 | ## Badge variants
29 | 
30 | - By default, badges don't have any icons attached and are set in medium size and in primary variant
31 | - Badges with icon are available only in xsmall and medium sizes
32 | - Badges with icon are available only in xsmall and medium sizes
33 | - Badges with icon are available only in medium sizes
34 | - Badges with success icon are available only in xsmall and medium sizes
35 | - Badges with both icon and success icon are available only in xsmall and medium sizes
36 | 
37 | ---
38 | 
39 | ## Accessibility
40 | 
41 | - The host sets `role="img"` for screen reader compatibility (interim solution until NVDA-compatible alternative is found).
42 | - The `aria-label` is automatically generated from the badge text content.
43 | - If `disabled` is true, the label is prefixed with `"Disabled badge: "`.
44 | - Otherwise, it's `"Badge: {text}"`.
45 | 
46 | ---
47 | 
48 | ## Test Coverage
49 | 
50 | The component is comprehensively tested using Angular CDK Testing and a custom `DsBadgeHarness`.
51 | 
52 | ### Functional Behavior
53 | 
54 | - Retrieves and verifies label text content
55 | - Filters badges by:
56 | - `size` (e.g., `'xsmall'`, `'medium'`)
57 | - `variant` (e.g., `'secondary'`)
58 | - label using a regex matcher
59 | - Validates dynamic input changes:
60 | - Updates to `size` correctly toggle size class
61 | - Changes to `variant` are reflected in DOM state
62 | 
63 | ### Slot Content
64 | 
65 | - Verifies rendering of text in the `[slot=start]` and `[slot=end]` containers
66 | - Confirms SVG elements are supported and rendered in the start slot
67 | 
68 | ### State & Styling
69 | 
70 | - Verifies toggling of the `inverse` class using `inverse` input
71 | - Confirms `ds-badge-disabled` class presence when `disabled` is set
72 | 
73 | ### Accessibility
74 | 
75 | - Checks that the computed `aria-label` accurately reflects the text and disabled state
76 | - Validates screen reader output using `@guidepup/virtual-screen-reader`
77 | 
```

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

```typescript
 1 | import * as fs from 'node:fs';
 2 | import * as path from 'node:path';
 3 | import {
 4 |   DsComponentsArraySchema,
 5 |   DsComponentSchema,
 6 | } from './ds-components.schema.js';
 7 | import { z } from 'zod';
 8 | import { loadDefaultExport } from '@push-based/utils';
 9 | 
10 | export type DsComponent = z.infer<typeof DsComponentSchema>;
11 | export type DsComponentsArray = z.infer<typeof DsComponentsArraySchema>;
12 | 
13 | export function validateDsComponent(rawComponent: unknown): DsComponent {
14 |   const validation = DsComponentSchema.safeParse(rawComponent);
15 |   if (!validation.success) {
16 |     throw new Error(
17 |       `Invalid component format: ${JSON.stringify(validation.error.format())}`,
18 |     );
19 |   }
20 |   return validation.data;
21 | }
22 | 
23 | export function validateDsComponentsArray(rawData: unknown): DsComponentsArray {
24 |   if (!Array.isArray(rawData)) {
25 |     throw new Error(`Expected array of components, received ${typeof rawData}`);
26 |   }
27 | 
28 |   const validatedComponents: DsComponent[] = [];
29 |   for (let i = 0; i < rawData.length; i++) {
30 |     try {
31 |       const validComponent = validateDsComponent(rawData[i]);
32 |       validatedComponents.push(validComponent);
33 |     } catch (ctx) {
34 |       throw new Error(`Component at index ${i}: ${(ctx as Error).message}`);
35 |     }
36 |   }
37 | 
38 |   const arrayValidation =
39 |     DsComponentsArraySchema.safeParse(validatedComponents);
40 |   if (!arrayValidation.success) {
41 |     throw new Error(
42 |       `Array validation failed: ${JSON.stringify(
43 |         arrayValidation.error.format(),
44 |       )}`,
45 |     );
46 |   }
47 | 
48 |   return arrayValidation.data;
49 | }
50 | 
51 | export async function loadAndValidateDsComponentsFile(
52 |   cwd: string,
53 |   deprecatedCssClassesPath: string,
54 | ): Promise<DsComponentsArray> {
55 |   if (
56 |     !deprecatedCssClassesPath ||
57 |     typeof deprecatedCssClassesPath !== 'string'
58 |   ) {
59 |     throw new Error('deprecatedCssClassesPath must be a string path');
60 |   }
61 | 
62 |   const absPath = path.resolve(cwd, deprecatedCssClassesPath);
63 |   if (!fs.existsSync(absPath)) {
64 |     throw new Error(`File not found at deprecatedCssClassesPath: ${absPath}`);
65 |   }
66 | 
67 |   try {
68 |     const rawData = await loadDefaultExport(absPath);
69 | 
70 |     return validateDsComponentsArray(rawData);
71 |   } catch (ctx) {
72 |     if (
73 |       ctx instanceof Error &&
74 |       (ctx.message.includes('Invalid component format') ||
75 |         ctx.message.includes('Expected array of components') ||
76 |         ctx.message.includes('Component at index'))
77 |     ) {
78 |       throw ctx;
79 |     }
80 |     throw new Error(
81 |       `Failed to load configuration file: ${(ctx as Error).message}`,
82 |     );
83 |   }
84 | }
85 | 
```

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

```json
 1 | {
 2 |   "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
 3 |   "version": 1,
 4 |   "newProjectRoot": "projects",
 5 |   "projects": {
 6 |     "minimal": {
 7 |       "projectType": "application",
 8 |       "schematics": {},
 9 |       "root": "",
10 |       "sourceRoot": "src",
11 |       "prefix": "app",
12 |       "architect": {
13 |         "build": {
14 |           "builder": "@angular-devkit/build-angular:application",
15 |           "options": {
16 |             "outputPath": "dist/minimal",
17 |             "index": "src/index.html",
18 |             "browser": "src/main.ts",
19 |             "polyfills": ["zone.js"],
20 |             "tsConfig": "tsconfig.app.json",
21 |             "assets": [
22 |               {
23 |                 "glob": "**/*",
24 |                 "input": "public"
25 |               }
26 |             ],
27 |             "styles": ["src/styles.css"],
28 |             "scripts": []
29 |           },
30 |           "configurations": {
31 |             "production": {
32 |               "budgets": [
33 |                 {
34 |                   "type": "initial",
35 |                   "maximumWarning": "500kB",
36 |                   "maximumError": "1MB"
37 |                 },
38 |                 {
39 |                   "type": "anyComponentStyle",
40 |                   "maximumWarning": "4kB",
41 |                   "maximumError": "8kB"
42 |                 }
43 |               ],
44 |               "outputHashing": "all"
45 |             },
46 |             "development": {
47 |               "optimization": false,
48 |               "extractLicenses": false,
49 |               "sourceMap": true
50 |             }
51 |           },
52 |           "defaultConfiguration": "production"
53 |         },
54 |         "serve": {
55 |           "builder": "@angular-devkit/build-angular:dev-server",
56 |           "configurations": {
57 |             "production": {
58 |               "buildTarget": "minimal:build:production"
59 |             },
60 |             "development": {
61 |               "buildTarget": "minimal:build:development"
62 |             }
63 |           },
64 |           "defaultConfiguration": "development"
65 |         },
66 |         "extract-i18n": {
67 |           "builder": "@angular-devkit/build-angular:extract-i18n"
68 |         },
69 |         "test": {
70 |           "builder": "@angular-devkit/build-angular:karma",
71 |           "options": {
72 |             "polyfills": ["zone.js", "zone.js/testing"],
73 |             "tsConfig": "tsconfig.spec.json",
74 |             "assets": [
75 |               {
76 |                 "glob": "**/*",
77 |                 "input": "public"
78 |               }
79 |             ],
80 |             "styles": ["src/styles.css"],
81 |             "scripts": []
82 |           }
83 |         }
84 |       }
85 |     }
86 |   }
87 | }
88 | 
```

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

```json
 1 | {
 2 |   "name": "@push-based/source",
 3 |   "version": "0.0.0",
 4 |   "type": "module",
 5 |   "license": "MIT",
 6 |   "scripts": {
 7 |     "publish:mcp": "nx build @push-based/angular-toolkit-mcp && cd packages/angular-mcp/dist && npm publish"
 8 |   },
 9 |   "private": true,
10 |   "devDependencies": {
11 |     "@eslint/js": "^9.28.0",
12 |     "@modelcontextprotocol/inspector": "^0.14.0",
13 |     "@nx/angular": "21.0.4",
14 |     "@nx/eslint": "21.0.4",
15 |     "@nx/eslint-plugin": "21.0.4",
16 |     "@nx/express": "21.0.4",
17 |     "@nx/jest": "21.0.4",
18 |     "@nx/js": "21.0.4",
19 |     "@nx/node": "21.0.4",
20 |     "@nx/vite": "21.0.4",
21 |     "@nx/web": "21.0.4",
22 |     "@nx/webpack": "21.0.4",
23 |     "@pmmmwh/react-refresh-webpack-plugin": "^0.5.16",
24 |     "@svgr/webpack": "^8.0.1",
25 |     "@swc-node/register": "~1.10.10",
26 |     "@swc/cli": "0.7.7",
27 |     "@swc/core": "~1.11.31",
28 |     "@swc/helpers": "~0.5.11",
29 |     "@swc/jest": "~0.2.38",
30 |     "@types/express": "^4.17.23",
31 |     "@types/jest": "^29.5.12",
32 |     "@types/node": "~18.16.20",
33 |     "@vitest/coverage-v8": "^3.2.3",
34 |     "@vitest/ui": "^3.2.3",
35 |     "eslint": "^9.28.0",
36 |     "eslint-config-prettier": "10.1.5",
37 |     "eslint-plugin-functional": "^9.0.2",
38 |     "eslint-plugin-unicorn": "^59.0.1",
39 |     "ignore-loader": "^0.1.2",
40 |     "jest": "^29.7.0",
41 |     "jest-environment-node": "^29.7.0",
42 |     "jiti": "2.4.2",
43 |     "jsdom": "~22.1.0",
44 |     "nx": "21.0.4",
45 |     "prettier": "^3.5.3",
46 |     "react-refresh": "^0.17.0",
47 |     "simple-git": "^3.28.0",
48 |     "ts-jest": "^29.3.4",
49 |     "ts-node": "10.9.2",
50 |     "tslib": "^2.3.0",
51 |     "typescript": "~5.7.2",
52 |     "typescript-eslint": "^8.34.0",
53 |     "vite": "^6.3.5",
54 |     "vitest": "^3.2.3",
55 |     "webpack-cli": "^6.0.1"
56 |   },
57 |   "workspaces": [
58 |     "packages/*",
59 |     "packages/server/app/*",
60 |     "packages/libs/*",
61 |     "packages/shared/*",
62 |     "shared"
63 |   ],
64 |   "dependencies": {
65 |     "@angular-devkit/schematics": "~19.2.0",
66 |     "@angular/cli": "~19.2.0",
67 |     "@angular/compiler": "~19.2.0",
68 |     "@code-pushup/core": "^0.75.0",
69 |     "@code-pushup/eslint-plugin": "^0.75.0",
70 |     "@code-pushup/models": "^0.75.0",
71 |     "@code-pushup/utils": "^0.75.0",
72 |     "@modelcontextprotocol/sdk": "^1.12.1",
73 |     "@push-based/ds-component-coverage": "^0.0.1",
74 |     "@push-based/models": "^0.0.1",
75 |     "@push-based/utils": "^0.0.1",
76 |     "@vue/language-core": "^2.2.10",
77 |     "axios": "^1.9.0",
78 |     "express": "^4.21.2",
79 |     "memfs": "^4.17.0",
80 |     "microdiff": "^1.5.0",
81 |     "postcss": "^8.5.4",
82 |     "postcss-safe-parser": "^7.0.1",
83 |     "simplegit": "^1.0.2",
84 |     "ts-morph": "^26.0.0",
85 |     "vite-plugin-dts": "^4.5.4",
86 |     "zod": "^3.25.57"
87 |   }
88 | }
89 | 
```

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

```typescript
 1 | import * as fs from 'fs';
 2 | import * as path from 'path';
 3 | import {
 4 |   validateComponentName,
 5 |   componentNameToTagName,
 6 |   componentNameToKebabCase,
 7 | } from '../../shared/utils/component-validation.js';
 8 | import { resolveCrossPlatformPath } from '../../shared/utils/cross-platform-path.js';
 9 | 
10 | export interface ComponentDocPaths {
11 |   componentName: string;
12 |   folderSlug: string;
13 |   tagName: string;
14 |   paths: { api: string; overview: string };
15 | }
16 | 
17 | export interface ComponentDocContent {
18 |   componentName: string;
19 |   tagName: string;
20 |   api: string | null;
21 |   overview: string | null;
22 | }
23 | 
24 | export function getComponentDocPathsForName(
25 |   docsBasePath: string,
26 |   componentName: string,
27 | ): ComponentDocPaths {
28 |   const folderSlug = componentNameToKebabCase(componentName);
29 |   const tagName = componentNameToTagName(componentName);
30 |   const base = path.join(docsBasePath, folderSlug);
31 |   return {
32 |     componentName,
33 |     folderSlug,
34 |     tagName,
35 |     paths: {
36 |       api: path.join(base, `${folderSlug}-tabs/api.mdx`),
37 |       overview: path.join(base, `${folderSlug}-tabs/overview.mdx`),
38 |     },
39 |   };
40 | }
41 | 
42 | export function enrichSingleComponentDoc(
43 |   doc: ComponentDocPaths,
44 | ): ComponentDocContent {
45 |   let apiContent = null;
46 |   let overviewContent = null;
47 | 
48 |   if (fs.existsSync(doc.paths.api)) {
49 |     apiContent = fs.readFileSync(doc.paths.api, 'utf-8');
50 |   }
51 |   if (fs.existsSync(doc.paths.overview)) {
52 |     overviewContent = fs.readFileSync(doc.paths.overview, 'utf-8');
53 |   }
54 | 
55 |   return {
56 |     componentName: doc.componentName,
57 |     tagName: doc.tagName,
58 |     api: apiContent,
59 |     overview: overviewContent,
60 |   };
61 | }
62 | 
63 | /**
64 |  * Reusable helper function to get component documentation
65 |  * @param componentName - The name of the component (e.g., DsButton)
66 |  * @param storybookDocsRoot - The root path to the storybook docs
67 |  * @param cwd - Current working directory (optional, defaults to process.cwd())
68 |  * @returns Component documentation with API and Overview content
69 |  * @throws Error if component validation fails or documentation retrieval fails
70 |  */
71 | export function getComponentDocs(
72 |   componentName: string,
73 |   storybookDocsRoot: string,
74 |   cwd: string = process.cwd(),
75 | ): ComponentDocContent {
76 |   try {
77 |     validateComponentName(componentName);
78 | 
79 |     const docsBasePath = resolveCrossPlatformPath(cwd, storybookDocsRoot);
80 |     const docPaths = getComponentDocPathsForName(docsBasePath, componentName);
81 |     const doc = enrichSingleComponentDoc(docPaths);
82 | 
83 |     if (!doc || (!doc.api && !doc.overview)) {
84 |       throw new Error(`No documentation found for component: ${componentName}`);
85 |     }
86 | 
87 |     return doc;
88 |   } catch (ctx) {
89 |     throw new Error(
90 |       `Error retrieving component documentation: ${(ctx as Error).message}`,
91 |     );
92 |   }
93 | }
94 | 
```

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

```markdown
 1 | import { Canvas } from '@storybook/blocks';
 2 | 
 3 | import * as SegmentedControlStories from '../segmented-control.component.stories';
 4 | 
 5 | 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.
 6 | 
 7 | <Canvas of={SegmentedControlStories.Default} />
 8 | 
 9 | ---
10 | 
11 | ## Usage
12 | 
13 | Import `DsSegmentedControlModule` in your component, apply `ds-segmented-control` and `ds-segmented-options` selectors in your template.
14 | 
15 | ```ts
16 | import { DsSegmentedControlModule } from '@frontend/ui/segmented-control'; // 👈 add to file imports
17 | 
18 | @Component({
19 |   imports: [DsSegmentedControlModule], // 👈 add to component imports
20 |   template: `...`,
21 | })
22 | export class AppComponent {}
23 | ```
24 | 
25 | ---
26 | 
27 | ## Additional Configuration
28 | 
29 | ### Segmented item width (max-width)
30 | 
31 | 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.
32 | 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:
33 | 
34 | - `--ds-segment-item-text-max-width`: Sets the max-width of the segment item. Default is `auto`.
35 | 
36 | To adjust the width, add the following CSS to your styles:
37 | 
38 | ```html
39 | <ds-segmented-control class="ds-segmented-control component-class-name">
40 |   <ds-segment-item>...</ds-segment-item>
41 | </ds-segmented-control>
42 | ```
43 | 
44 | ```css
45 | .component-class-name {
46 |   --ds-segment-item-text-max-width: 100px;
47 | }
48 | ```
49 | 
50 | ---
51 | 
52 | ## Accessibility
53 | 
54 | - The `role` attribute on the segmented control is dynamically set to `tablist` or `radiogroup`.
55 | - Each `ds-segmented-option`:
56 | - Uses `role="tab"` or `role="radio"` depending on control type
57 | - Has `aria-selected` or `aria-checked` to reflect selection state
58 | - Includes `aria-label` using the option's `title` or `name`
59 | - Uses `tabindex="0"` for selected, `-1` for others
60 | - Supports keyboard navigation:
61 | - `ArrowLeft` / `ArrowRight` for focus movement
62 | - `Enter` / `Space` to activate
63 | - Screen reader support confirmed via virtual-screen-reader tests
64 | 
65 | ---
66 | 
67 | ## Test Coverage
68 | 
69 | - Loads and renders segmented control and its options
70 | - Selects options using:
71 | - `selectTabByText`
72 | - `selectTabByName`
73 | - Supports toggling `fullWidth` and `inverse` inputs
74 | - Switches between `tablist` and `radiogroup` roles
75 | - Emits `activeOptionChange` when selection changes
76 | - Keyboard navigation (arrow keys, enter, space)
77 | - All roles and ARIA states validated for accessibility
78 | - Screen reader flows tested using `@guidepup/virtual-screen-reader`
79 | 
```

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

```typescript
 1 | /**
 2 |  * Utility functions for contract listing and file operations
 3 |  */
 4 | 
 5 | import type { ContractFileInfo } from '../models/types.js';
 6 | 
 7 | /**
 8 |  * Extracts component name from contract filename
 9 |  * Handles pattern: componentName-timestamp.contract.json
10 |  */
11 | export function extractComponentNameFromFile(fileName: string): string {
12 |   const baseName = fileName.replace('.contract.json', '');
13 |   const parts = baseName.split('-');
14 | 
15 |   if (parts.length > 2) {
16 |     let timestampStartIndex = -1;
17 |     for (let i = 1; i < parts.length; i++) {
18 |       if (/^\d{4}$/.test(parts[i])) {
19 |         timestampStartIndex = i;
20 |         break;
21 |       }
22 |     }
23 | 
24 |     if (timestampStartIndex > 0) {
25 |       return parts.slice(0, timestampStartIndex).join('-');
26 |     }
27 |   }
28 | 
29 |   return parts[0] || 'unknown';
30 | }
31 | 
32 | /**
33 |  * Formats byte size into human-readable format
34 |  */
35 | export function formatBytes(bytes: number): string {
36 |   if (bytes === 0) return '0 B';
37 | 
38 |   const k = 1024;
39 |   const sizes = ['B', 'KB', 'MB'];
40 |   const i = Math.floor(Math.log(bytes) / Math.log(k));
41 | 
42 |   return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i];
43 | }
44 | 
45 | /**
46 |  * Converts timestamp to human-readable "time ago" format
47 |  */
48 | export function getTimeAgo(timestamp: string): string {
49 |   const now = new Date();
50 |   const past = new Date(timestamp);
51 |   const diffMs = now.getTime() - past.getTime();
52 | 
53 |   const minutes = Math.floor(diffMs / (1000 * 60));
54 |   const hours = Math.floor(diffMs / (1000 * 60 * 60));
55 |   const days = Math.floor(diffMs / (1000 * 60 * 60 * 24));
56 | 
57 |   if (minutes < 1) return 'just now';
58 |   if (minutes < 60) return `${minutes}m ago`;
59 |   if (hours < 24) return `${hours}h ago`;
60 |   if (days < 7) return `${days}d ago`;
61 | 
62 |   return past.toLocaleDateString();
63 | }
64 | 
65 | /**
66 |  * Groups contracts by component name and formats them for display output
67 |  */
68 | export function formatContractsByComponent(
69 |   contracts: ContractFileInfo[],
70 | ): string[] {
71 |   const output: string[] = [];
72 | 
73 |   const contractsByComponent = new Map<string, ContractFileInfo[]>();
74 |   contracts.forEach((contract) => {
75 |     const componentContracts =
76 |       contractsByComponent.get(contract.componentName) || [];
77 |     componentContracts.push(contract);
78 |     contractsByComponent.set(contract.componentName, componentContracts);
79 |   });
80 | 
81 |   contractsByComponent.forEach((componentContracts, componentName) => {
82 |     output.push(`🎯 ${componentName}:`);
83 | 
84 |     componentContracts.forEach((contract) => {
85 |       const timeAgo = getTimeAgo(contract.timestamp);
86 |       output.push(`   📄 ${contract.fileName}`);
87 |       output.push(`      🔗 ${contract.filePath}`);
88 |       output.push(`      ⏱️  ${timeAgo}`);
89 |       output.push(`      🔑 ${contract.hash.substring(0, 12)}...`);
90 |       output.push(`      📊 ${contract.size}`);
91 |     });
92 | 
93 |     output.push('');
94 |   });
95 | 
96 |   return output;
97 | }
98 | 
```
Page 3/10FirstPrevNextLast