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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ A Model Context Protocol (MCP) server that provides Angular project analysis and
- **Dependency Mapping**: Map component dependencies across modules, templates, and styles
- **ESLint Integration**: Lint Angular files with automatic ESLint configuration discovery
- **Project Analysis**: Analyze buildable/publishable libraries and validate import paths
- **Component Documentation**: Retrieve component data and documentation
- **Component Documentation**: Retrieve component data and documentation, list available components

## Use Cases

Expand Down Expand Up @@ -135,6 +135,8 @@ my-angular-workspace/

### Component Analysis

- **`list-ds-components`**: List all available Design System components in the project with their file paths and metadata

- **`get-ds-component-data`**: Return data for a component including implementation files, documentation files, and import path

- **`build-component-usage-graph`**: Maps where given Angular components are imported (modules, specs, templates, styles) so refactors touch every file
Expand Down
7 changes: 6 additions & 1 deletion docs/component-refactoring-flow.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ This document describes a 3-step AI-assisted component refactoring process for i
2. **Refactor Component** → Execute approved checklist items and implement changes
3. **Validate Component** → Verify improvements through contract comparison and scoring

The process includes two quality gates where human review and approval are required.
The process includes two quality gates where human review and approval are required. When refactoring involves Design System components, the process can leverage selective data retrieval to access only the specific component information needed (implementation, documentation, or stories).

## Prerequisites

Expand Down Expand Up @@ -159,6 +159,11 @@ At this point, all checklist items have been processed. You must review the refa
- Returns: contract path with component's public API, DOM structure, and styles
- Purpose: Establish baseline for validation comparison

- `get-ds-component-data` - Retrieves Design System component information when needed
- Parameters: `componentName`, `sections` (optional) - Array of sections to include: "implementation", "documentation", "stories", "all"
- Returns: Selective component data based on refactoring needs
- Purpose: Access DS component documentation and examples for proper implementation patterns

### Flow

> You don't need to manually perform any of the listed actions except providing the initial parameters.
Expand Down
12 changes: 12 additions & 0 deletions docs/ds-refactoring-flow.md
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,13 @@ Before proceeding to Phase 3, you must review the comprehensive migration plan.

### Tools used

- `list-ds-components` - Lists all available Design System components in the project
- Parameters: `sections` (optional) - Array of sections to include: "implementation", "documentation", "stories", "all"
- Returns: Complete inventory of DS components with their file paths and metadata
- Provides: Overview of available components for comprehensive migration planning

- `get-ds-component-data` - Retrieves comprehensive component information for all involved components
- Parameters: `componentName`, `sections` (optional) - Array of sections to include: "implementation", "documentation", "stories", "all"
- Returns: Complete implementation files, documentation files, import paths for multiple components
- Provides: Component source code, API documentation, usage examples across scope

Expand Down Expand Up @@ -285,7 +291,13 @@ Before proceeding to Phase 3, you must review the migration plan.

### Tools used

- `list-ds-components` - Lists all available Design System components in the project
- Parameters: `sections` (optional) - Array of sections to include: "implementation", "documentation", "stories", "all"
- Returns: Complete inventory of DS components with their file paths and metadata
- Provides: Overview of available components for migration planning

- `get-ds-component-data` - Retrieves comprehensive component information
- Parameters: `componentName`, `sections` (optional) - Array of sections to include: "implementation", "documentation", "stories", "all"
- Returns: implementation files, documentation files, import paths
- Provides: component source code, API documentation, usage examples

Expand Down
32 changes: 26 additions & 6 deletions docs/tools.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,23 @@ This document provides comprehensive guidance for AI agents working with Angular

### 📚 Component Information Tools

#### `list-ds-components`
**Purpose**: Lists all available Design System components in the project with their file paths and metadata
**AI Usage**: Discover available DS components before starting migration or analysis workflows
**Key Parameters**:
- `sections`: Array of sections to include - `"implementation"`, `"documentation"`, `"stories"`, or `"all"` (default: `["all"]`)
**Output**: Complete inventory of DS components with their implementation files, documentation files, stories files, and import paths
**Best Practice**: Use as the first step to understand the DS component landscape before targeted analysis

#### `get-ds-component-data`
**Purpose**: Returns comprehensive data for a specific DS component including implementation files, documentation files, stories files, and import path
**AI Usage**: Get detailed information about a specific component for analysis or migration planning
**Key Parameters**:
- `componentName`: DS component class name (e.g., `DsBadge`)
- `sections`: Array of sections to include - `"implementation"`, `"documentation"`, `"stories"`, or `"all"` (default: `["all"]`)
**Output**: Structured data with file paths for implementation, documentation, stories, and import information
**Best Practice**: Use selective sections to optimize performance when you only need specific types of files

#### `get-component-docs`
**Purpose**: Retrieves MDX documentation for DS components
**AI Usage**: Access official component documentation to understand proper usage patterns
Expand Down Expand Up @@ -127,16 +144,18 @@ This document provides comprehensive guidance for AI agents working with Angular

### 1. Discovery & Analysis Workflow
```
1. report-violations → Identify all violations
2. get-project-dependencies → Analyze project structure
1. list-ds-components → Discover available DS components
2. report-violations → Identify all violations
3. get-project-dependencies → Analyze project structure
```

### 2. Planning & Preparation Workflow
```
1. build-component-usage-graph → Map component relationships
2. get-component-docs → Review proper usage patterns
3. get-component-paths → Verify import paths
4. build_component_contract → Create baseline contracts
1. get-ds-component-data → Get comprehensive component information
2. build-component-usage-graph → Map component relationships
3. get-component-docs → Review proper usage patterns
4. get-component-paths → Verify import paths
5. build_component_contract → Create baseline contracts
```

### 3. Refactoring & Validation Workflow
Expand Down Expand Up @@ -173,6 +192,7 @@ This document provides comprehensive guidance for AI agents working with Angular

- Use `groupBy: "folder"` for large codebases to reduce output size
- Limit `violationFiles` arrays to relevant files only
- Use selective `sections` parameter in `get-ds-component-data` and `list-ds-components` to retrieve only needed data types
- Cache component documentation between related operations
- Run validation tools in parallel when possible

Expand Down
Original file line number Diff line number Diff line change
@@ -1,26 +1,56 @@
import { ToolSchemaOptions } from '@push-based/models';
import { createHandler } from '../shared/utils/handler-helpers.js';
import {
createComponentInputSchema,
COMMON_ANNOTATIONS,
} from '../shared/models/schema-helpers.js';
createHandler,
BaseHandlerOptions,
} from '../shared/utils/handler-helpers.js';
import { COMMON_ANNOTATIONS } from '../shared/models/schema-helpers.js';
import { getComponentPathsInfo } from './utils/paths-helpers.js';
import { getComponentDocPathsForName } from './utils/doc-helpers.js';
import { validateComponentName } from '../shared/utils/component-validation.js';
import {
validateComponentName,
componentNameToKebabCase,
} from '../shared/utils/component-validation.js';
import { resolveCrossPlatformPath } from '../shared/utils/cross-platform-path.js';
import * as fs from 'fs';
import * as path from 'path';

interface DsComponentDataOptions {
interface DsComponentDataOptions extends BaseHandlerOptions {
componentName: string;
sections?: string[];
}

interface DsComponentData {
componentName: string;
implementation: string[];
documentation: string[];
stories: string[];
importPath: string;
}

export const getDsComponentDataToolSchema: ToolSchemaOptions = {
name: 'get-ds-component-data',
description: `Return comprehensive data for a DS component including implementation files, documentation files, and import path.`,
inputSchema: createComponentInputSchema(
'The class name of the component to get data for (e.g., DsBadge)',
),
description: `Return comprehensive data for a DS component including implementation files, documentation files, stories files, and import path.`,
inputSchema: {
type: 'object',
properties: {
componentName: {
type: 'string',
description:
'The class name of the component to get data for (e.g., DsBadge)',
},
sections: {
type: 'array',
items: {
type: 'string',
enum: ['implementation', 'documentation', 'stories', 'all'],
},
description:
'Sections to include in the response. Options: "implementation", "documentation", "stories", "all". Defaults to ["all"] if not specified.',
default: ['all'],
},
},
required: ['componentName'],
},
annotations: {
title: 'Get Design System Component Data',
...COMMON_ANNOTATIONS.readOnly,
Expand All @@ -45,7 +75,7 @@ function getAllFilesInDirectory(dirPath: string): string[] {
}
}
} catch {
// Ignore directories that can't be read
return;
}
}

Expand All @@ -56,38 +86,89 @@ function getAllFilesInDirectory(dirPath: string): string[] {
return files;
}

function findStoriesFiles(componentPath: string): string[] {
const storiesFiles: string[] = [];

try {
if (fs.existsSync(componentPath)) {
const items = fs.readdirSync(componentPath);

for (const item of items) {
const fullPath = path.join(componentPath, item);
const stat = fs.statSync(fullPath);

if (stat.isFile() && item.endsWith('.stories.ts')) {
storiesFiles.push(fullPath);
}
}
}
} catch {
return storiesFiles;
}

return storiesFiles;
}

export const getDsComponentDataHandler = createHandler<
DsComponentDataOptions,
any
DsComponentData
>(
getDsComponentDataToolSchema.name,
async ({ componentName }, { cwd, uiRoot, storybookDocsRoot }) => {
async (
{ componentName, sections = ['all'] },
{ cwd, uiRoot, storybookDocsRoot },
) => {
try {
validateComponentName(componentName);

// Get component paths info
const pathsInfo = getComponentPathsInfo(componentName, uiRoot, cwd);
const includeAll = sections.includes('all');
const includeImplementation =
includeAll || sections.includes('implementation');
const includeDocumentation =
includeAll || sections.includes('documentation');
const includeStories = includeAll || sections.includes('stories');

// Get all implementation files in src directory
const srcFiles = getAllFilesInDirectory(pathsInfo.srcPath);
const implementationFiles = srcFiles.map((file) => `file://${file}`);
const pathsInfo = getComponentPathsInfo(componentName, uiRoot, cwd);

// Get documentation paths
const docsBasePath = resolveCrossPlatformPath(cwd, storybookDocsRoot);
const docPaths = getComponentDocPathsForName(docsBasePath, componentName);
let implementationFiles: string[] = [];
if (includeImplementation) {
const srcFiles = getAllFilesInDirectory(pathsInfo.srcPath);
implementationFiles = srcFiles.map((file) => `file://${file}`);
}

const documentationFiles: string[] = [];
if (fs.existsSync(docPaths.paths.api)) {
documentationFiles.push(`file://${docPaths.paths.api}`);
if (includeDocumentation) {
const docsBasePath = resolveCrossPlatformPath(cwd, storybookDocsRoot);
const docPaths = getComponentDocPathsForName(
docsBasePath,
componentName,
);

if (fs.existsSync(docPaths.paths.api)) {
documentationFiles.push(`file://${docPaths.paths.api}`);
}
if (fs.existsSync(docPaths.paths.overview)) {
documentationFiles.push(`file://${docPaths.paths.overview}`);
}
}
if (fs.existsSync(docPaths.paths.overview)) {
documentationFiles.push(`file://${docPaths.paths.overview}`);

let storiesFilePaths: string[] = [];
if (includeStories) {
const docsBasePath = resolveCrossPlatformPath(cwd, storybookDocsRoot);
const componentFolderName = componentNameToKebabCase(componentName);
const storiesComponentFolderPath = path.join(
docsBasePath,
componentFolderName,
);
const storiesFiles = findStoriesFiles(storiesComponentFolderPath);
storiesFilePaths = storiesFiles.map((file) => `file://${file}`);
}

return {
componentName,
implementation: implementationFiles,
documentation: documentationFiles,
stories: storiesFilePaths,
importPath: pathsInfo.importPath,
};
} catch (ctx) {
Expand All @@ -99,7 +180,6 @@ export const getDsComponentDataHandler = createHandler<
(result) => {
const messages: string[] = [];

// Implementation section
if (result.implementation && result.implementation.length > 0) {
messages.push('Implementation');
messages.push('');
Expand All @@ -109,7 +189,6 @@ export const getDsComponentDataHandler = createHandler<
messages.push('');
}

// Documentation section
if (result.documentation && result.documentation.length > 0) {
messages.push('Documentation');
messages.push('');
Expand All @@ -119,7 +198,15 @@ export const getDsComponentDataHandler = createHandler<
messages.push('');
}

// Import path section
if (result.stories && result.stories.length > 0) {
messages.push('Stories');
messages.push('');
result.stories.forEach((file: string) => {
messages.push(file);
});
messages.push('');
}

if (result.importPath) {
messages.push('Import path');
messages.push('');
Expand Down
Loading