Skill
migration-lint
Comprehensive Angular migration compliance checker with auto-fix capabilities. Use when reviewing migrated code for DDD architecture, Angular 20 syntax, and UI pattern compliance.
From one-ui-migrationInstall
1
Run in your terminal$
npx claudepluginhub michael0520/milo-claudekit --plugin one-ui-migrationTool Access
This skill uses the workspace's default tool permissions.
Skill Content
Migration Lint Command
Review and fix migrated Angular code for compliance. This command reads ALL migration guidelines and performs a comprehensive checklist review.
Arguments
$ARGUMENTS- Target path to review (e.g.,libs/mxsecurity/connection-management-page)
Workflow
Step 1: Load ALL Migration Guidelines
IMPORTANT: Read the core SKILL.md first, then reference documents as needed from ``.
Core: one-ui-migration skill
| # | Topic | File | Category |
|---|---|---|---|
| 1 | Migration Context | rules/reference/migration-context.md | Foundation |
| 2 | Angular 20 Syntax | rules/reference/angular-20-syntax.md | Foundation |
| 3 | DDD Architecture | rules/reference/ddd-architecture.md | Architecture |
| 4 | State Management | rules/reference/state-management.md | Architecture |
| 5 | Authentication | rules/tools/auth.md | Architecture |
| 6 | Migration Checklist | rules/reference/migration-checklist.md | Foundation |
| 7 | Pitfalls Index | rules/reference/pitfalls/index.md | Pitfalls |
| 7a | Pitfalls: Translation & Layout | rules/reference/pitfalls/translation-layout.md | Pitfalls |
| 7b | Pitfalls: DDD Violations | rules/reference/pitfalls/ddd-violations.md | Pitfalls |
| 7c | Pitfalls: Angular Syntax | rules/reference/pitfalls/angular-syntax.md | Pitfalls |
| 7d | Pitfalls: Forms & Services | rules/reference/pitfalls/forms-services.md | Pitfalls |
| 8 | Validators | rules/tools/forms/validators.md | Forms |
| 9 | Error Handling | rules/tools/forms/error-handling.md | Forms |
| 10 | Form Patterns | rules/tools/forms/patterns.md | Forms |
| 11 | Page Layout | rules/tools/ui/page-layout.md | UI Patterns |
| 12a | Forms | rules/tools/ui/forms.md | UI Patterns |
| 12b | Buttons | rules/tools/ui/buttons.md | UI Patterns |
| 12c | Components | rules/tools/ui/components.md | UI Patterns |
| 13 | Dialogs | rules/tools/ui/dialogs.md | UI Patterns |
| 14 | Table Basics | rules/tools/tables/basics.md | Tables |
| 15 | Table Columns | rules/tools/tables/columns.md | Tables |
| 16 | Table Advanced | rules/tools/tables/advanced.md | Tables |
| 17 | API Type Definitions | rules/reference/api-types.md | API |
| 18 | Shared Stores | rules/reference/shared-stores.md | State |
Step 2: Read Target Files
Read all .ts, .html, and .scss files in the target path:
{target}/domain/src/lib/**/*.ts{target}/features/src/lib/**/*.ts{target}/features/src/lib/**/*.html{target}/features/src/lib/**/*.scss{target}/ui/src/lib/**/*.ts{target}/ui/src/lib/**/*.html{target}/ui/src/lib/**/*.scss{target}/shell/src/lib/**/*.ts
Step 3: Comprehensive Checklist Review
Check EVERY item from EVERY migration file:
Checklist by Category
Category 1: Foundation & Pitfalls
migration-context.md
- Project follows one-ui monorepo structure
- Uses correct scope tags (
scope:mxsecurity) - Library types match DDD pattern (
domain,features,ui,shell)
angular-syntax.md
- Uses
@ifinstead of*ngIf - Uses
@for (item of items; track item.id)instead of*ngFor - Uses
@switchinstead ofngSwitch - Uses
inject()instead of constructor injection - Uses
input()instead of@Input() - Uses
output()instead of@Output() - Uses
viewChild()/viewChildren()instead of@ViewChild()/@ViewChildren() - Uses
contentChild()/contentChildren()instead of@ContentChild()/@ContentChildren() - Uses
model()for two-way binding where appropriate - Components are standalone (default in Angular 20)
- Uses
ChangeDetectionStrategy.OnPush
migration-checklist.md
- Domain layer contains only business logic, models, services, state
- Features layer contains smart components with store injection
- UI layer contains presentational components with input/output ONLY (NO store injection)
- Shell layer contains routing configuration
- Barrel exports (
index.ts) are properly configured
pitfalls/translation-layout.md
- Translation keys match source exactly (no new keys created)
- Form layout matches source (row groupings preserved)
- No padding in page component styles
- Page uses
gl-page-contentclass (not ng-container) - Table toolbar button order: Refresh → Create/Delete (only if old code has refresh)
pitfalls/ddd-violations.md
- No form templates in features layer (extract to UI)
- No store injection in UI components
- Dialogs are in features layer (not UI)
- No business logic in features (move to domain)
- No HTTP calls in UI components
pitfalls/angular-syntax.md
- No mixing old/new control flow (
*ngIfwith@for) - All
@forloops havetrack - No NgModule patterns (use standalone)
-
langChanged()called at field level (not in computed) - Uses
inject()instead of constructor injection
pitfalls/forms-services.md
-
mutationMethod/queryMethodimported from@one-ui/mxsecurity/shared/domain(NOTshared/util) - No unnecessary
.def.tsfiles for constants - Uses
MxSnackbarService(notMatSnackBar) - Uses
controls.xxx(not.get('xxx')) - Form valueChanges uses
toSignal()in effects - Uses
EMPTY_DASHconstant (notutils.emptyDash) - Uses
oneUiNumberOnly(notappNumberOnly) - Uses
mx-password-inputfor password fields - Uses
mx-key-valuefor readonly displays - No
TRANSLOCO_SCOPEprovider (use global translation keys) - No endpoint constants (
#ENDPOINTS = {}) - inline URLs directly in API methods
Category 2: Architecture
ddd-architecture.md
- Domain layer has correct structure
- Features layer has correct structure
- UI layer has correct structure
- Shell layer has correct structure
- Dependencies flow correctly (UI → Domain, Features → UI/Domain)
- No UI components importing from features
- Shared domain utilities used where appropriate
state-management.md
- Uses NgRx SignalStore for state management
- Store follows correct naming convention (
XxxStore) - Uses
withEntities()for entity management - Uses
withRequestStatus()for loading states - Uses
withDataService()for API integration - Effects handle success/error properly
- Uses
patchState()correctly - Store is provided in correct scope
authentication.md
- Uses
AuthStorefor authentication state - Permission checks use correct patterns
- Protected routes have guards
- API calls include proper authentication headers
Category 3: Forms (forms/)
validators.md
- Uses
OneValidators.required(notValidators.required) - Uses
OneValidators.email(notValidators.email) - Uses
OneValidators.minLength(n)(notValidators.minLength) - Uses
OneValidators.maxLength(n)(notValidators.maxLength) - Uses
OneValidators.min(n)(notValidators.min) - Uses
OneValidators.max(n)(notValidators.max) - Uses
OneValidators.pattern(x)(notValidators.pattern) - Custom validators follow correct patterns
- Cross-field validators implemented correctly
- Import from
@one-ui/mxsecurity/shared/domain
error-handling.md
- Form errors displayed using
<mat-error> - Error messages use translation keys
- Async validators handle errors
- API errors displayed to user
- Uses
MxSnackbarServicefor notifications
patterns.md
- Uses
NonNullableFormBuilder(notFormBuilder) - Form controls accessed via
controls.xxx(not.get('xxx')) -
getRawValue()used for form submission - Dynamic forms use
FormArraycorrectly - Form reset/patch follows patterns
- Forms in UI layer use
input()/output()for data flow
Category 4: UI Patterns (ui/)
ui/page-layout.md
- Root wrapper has
class="gl-page-content" - Uses
<one-ui-breadcrumb />at top - Uses
<mx-page-title [title]="t('...')">for page title - Content wrapped in
class="content-wrapper"(NOTmat-card) - Uses
*transloco="let t"pattern - Section titles use
class="gl-title-md"
buttons.md
- Submit button uses
mat-flat-button color="primary" - Cancel button uses
mat-button - Toolbar buttons use
mat-stroked-button - NO
mat-raised-button(usemat-flat-button) - Loading state uses
[mxButtonIsLoading]directive - Disabled state properly handles
noPermission()
forms.md
- Single form field NOT wrapped in
<div class="form-row"> - Multiple fields on same row use
<div class="form-row">
dialogs.md
- Uses shared dialog config (
smallDialogConfig,mediumDialogConfig,largeDialogConfig) - Has
viewContainerRef: this.#viewContainerRefwhen dialog uses store -
ConfirmDialogComponentresult checked as boolean (NOTresult?.confirm) - Dialog data typed properly
- Dialog returns correct type
-
afterClosed()subscription cleaned up properly
Category 5: Tables (tables/)
basics.md
- Uses
<mx-table>component - Column definitions use
MxColumnDef[] - Has
data-testidattributes - Loading state shows skeleton
- Empty state handled properly
columns.md
- Column types match data
- Custom templates import
MatTableModuleandMatSortModule - Link columns use proper navigation
- Status columns use proper styling
- Action columns positioned correctly
advanced.md
- Selection implemented correctly (if applicable)
- Sorting configured properly
- Pagination implemented (if applicable)
- Filtering works correctly (if applicable)
- Toolbar buttons use
mat-stroked-button
Category 6: API & Services
api-types.md
- API types defined in domain layer
- Request/Response types properly typed
- Uses proper HTTP methods
- Error responses typed
- Enums defined for constants
- NO endpoint constants (
#ENDPOINTS = {...}) - inline URL strings directly in methods
shared-stores.md
- Uses shared stores where appropriate (e.g.,
TimeZoneStore) - Shared store initialization handled
- Cross-feature state accessed via shared stores
- No duplicate state management
Category 7: Additional Checks
Translation (Transloco)
- Uses
*transloco="let t"or@let t = translocoT - NO
translatepipe - Translation keys follow naming convention
- Parameters passed correctly to
t()
Directives
-
oneUiNumberOnly(notappNumberOnly) - Custom directives use
one-uiprefix
Services
-
MxSnackbarService(notMatSnackBar) - Snackbar:
snackBar.open(msg)(notsnackBar.open(msg, '', { duration: 3000 }))
Constants
-
EMPTY_DASHfrom@one-ui/mxsecurity/shared/domain(notutils.emptyDash)
Imports
- No circular imports
- Imports from correct library paths
- No relative imports crossing library boundaries
Password Fields
- Uses
<mx-password-input>for password fields
Readonly Display
- Uses
<mx-key-value>for readonly key-value displays
Step 4: Fix Issues
Auto-fix the following issues:
| Issue | Fix |
|---|---|
mat-raised-button | → mat-flat-button |
*ngIf | → @if |
*ngFor | → @for (item of items; track item.id) |
Validators.required | → OneValidators.required |
Validators.email | → OneValidators.email |
Validators.minLength(n) | → OneValidators.minLength(n) |
Validators.maxLength(n) | → OneValidators.maxLength(n) |
.get('xxx') | → controls.xxx |
class="section-title" | → class="gl-title-md" |
appNumberOnly | → oneUiNumberOnly |
this.utils.emptyDash | → EMPTY_DASH |
readonly #ENDPOINTS = { | → Remove constant, inline URLs directly in each method |
from '@one-ui/mxsecurity/shared/util' (for mutationMethod/queryMethod) | → from '@one-ui/mxsecurity/shared/domain' |
Step 5: Generate Report
Output a compliance report:
## Migration Lint Report
**Path:** {target_path}
**Date:** {current_date}
### Checklist Summary
| Category | Files Checked | Pass | Fail | N/A |
|----------|---------------|------|------|-----|
| Foundation (01, 02, 06) | 3 | Pass | Fail | - |
| Architecture (03, 04, 05) | 3 | Pass | Fail | - |
| Pitfalls (08, 08a, 08b, 08c, 08d) | 5 | Pass | Fail | - |
| Forms (09a, 09b, 09c) | 3 | Pass | Fail | - |
| UI Patterns (10a, 10b, 10c) | 3 | Pass | Fail | - |
| Tables (11a, 11b, 11c) | 3 | Pass | Fail | - |
| API & Services (12, 13) | 2 | Pass | Fail | - |
| **Total** | **22** | **X** | **Y** | **Z** |
### Issues Fixed
| File | Line | Issue | Fix Applied |
|------|------|-------|-------------|
| component.html | 15 | mat-raised-button | Changed to mat-flat-button |
### Issues Found (Manual Review Needed)
| File | Line | Issue | Recommendation | Guide Ref |
|------|------|-------|----------------|-----------|
| component.ts | 42 | Missing viewContainerRef | Add viewContainerRef to dialog config | dialogs.md |
| store.ts | 20 | Store in UI layer | Move to features layer | ddd-architecture.md |
### Detailed Checklist Results
#### migration-context.md (Pass)
- [x] Project follows one-ui monorepo structure
- [x] Uses correct scope tags
#### angular-syntax.md (Warning)
- [x] Uses @if instead of *ngIf
- [ ] Uses inject() instead of constructor injection (line 45)
... (continue for all files)
### Summary
- **Migration Files Checked:** 22/22
- **Auto-fixed:** X issues
- **Manual review needed:** Y issues
- **Passed checks:** Z items
- **Failed checks:** W items
Quick Reference
Button Types
<!-- Form submit button -->
<button mat-flat-button color="primary" type="submit">Apply</button>
<!-- Table toolbar button -->
<button mat-stroked-button>Create</button>
<!-- Cancel/secondary button -->
<button mat-button type="button">Cancel</button>
Page Layout
<div *transloco="let t" class="gl-page-content">
<one-ui-breadcrumb />
<mx-page-title [title]="t('page.title')" />
<div class="content-wrapper">
<!-- content here, NO mat-card -->
</div>
</div>
Form Row
<!-- Single field - NO form-row wrapper -->
<mat-form-field>
<mat-label>{{ t('label') }}</mat-label>
<input matInput formControlName="field" />
</mat-form-field>
<!-- Multiple fields on same row - USE form-row wrapper -->
<div class="form-row">
<mat-form-field>...</mat-form-field>
<mat-form-field>...</mat-form-field>
</div>
Dialog Config
import { mediumDialogConfig } from '@one-ui/mxsecurity/shared/domain';
this.#dialog.open(MyDialog, {
...mediumDialogConfig,
data: dialogData,
viewContainerRef: this.#viewContainerRef // Required if dialog uses store
});
Validators
import { OneValidators } from '@one-ui/mxsecurity/shared/domain';
this.#fb.group({
name: ['', [OneValidators.required, OneValidators.maxLength(32)]],
email: ['', [OneValidators.required, OneValidators.email]],
port: ['', [OneValidators.required, OneValidators.min(1), OneValidators.max(65535)]]
});
ConfirmDialogComponent
// ConfirmDialogComponent returns boolean, NOT { confirm: boolean }
const dialogRef = this.#dialog.open<ConfirmDialogComponent, ConfirmDialogData, boolean>(
ConfirmDialogComponent,
{
...mediumDialogConfig,
viewContainerRef: this.#viewContainerRef,
data: { title: '...', desc: '...' }
}
);
dialogRef.afterClosed().subscribe((result) => {
if (result) { // result is boolean
this.doSomething();
}
});
Snackbar Service
// Use MxSnackbarService instead of MatSnackBar
import { MxSnackbarService } from '@moxa/formoxa/mx-snackbar';
readonly #snackBar = inject(MxSnackbarService);
// Simple usage
this.#snackBar.open('Success message');
// Persistent message (no auto-dismiss)
this.#snackBar.open('Saving...', { duration: 0 });
Section Title
<!-- Use gl-title-md class (global style) -->
<h3 class="gl-title-md">{{ t('section.title') }}</h3>
<!-- NOT section-title (requires custom SCSS) -->
<h3 class="section-title">{{ t('section.title') }}</h3>
Form Controls Access
// .get() - no type safety
const value = this.form.get('username')?.value;
// controls.xxx - type safe
const value = this.form.controls.username.value;
// In ValidatorFn - use type assertion
#myValidator(): ValidatorFn {
return (formGroup: AbstractControl): null => {
const controls = (formGroup as typeof this.form).controls;
const fieldValue = controls.fieldName.value;
return null;
};
}
Password Input
<!-- Use mx-password-input for password fields -->
<mx-password-input
formControlName="password"
[maxlength]="32"
></mx-password-input>
Key-Value Display
<!-- Use mx-key-value for readonly displays -->
<mx-key-value [key]="t('label')" [value]="displayValue"></mx-key-value>
UI Component Pattern
// UI components use input()/output() ONLY - NO store injection
export class MyUiComponent {
readonly data = input<MyData>();
readonly isLoading = input<boolean>(false);
readonly save = output<MyData>();
readonly cancel = output<void>();
}
Features Component Pattern
// Features components can inject stores
export class MyFeatureComponent {
readonly #store = inject(MyStore);
readonly data = this.#store.data;
readonly isLoading = this.#store.isLoading;
}
Similar Skills
Stats
Parent Repo Stars0
Parent Repo Forks0
Last CommitJan 24, 2026