angular-agents-skills 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +205 -0
  3. package/adapters/claude/index.ts +53 -0
  4. package/adapters/codex/index.ts +55 -0
  5. package/adapters/copilot/index.ts +45 -0
  6. package/adapters/cursor/index.ts +51 -0
  7. package/adapters/opencode/index.ts +63 -0
  8. package/agents/angular-architect/agent.md +143 -0
  9. package/agents/angular-architect/configs/claude.yaml +3 -0
  10. package/agents/angular-architect/configs/codex.yaml +2 -0
  11. package/agents/angular-architect/configs/opencode.yaml +5 -0
  12. package/agents/angular-migrator/agent.md +146 -0
  13. package/agents/angular-migrator/configs/claude.yaml +3 -0
  14. package/agents/angular-migrator/configs/codex.yaml +2 -0
  15. package/agents/angular-migrator/configs/opencode.yaml +5 -0
  16. package/agents/angular-reviewer/agent.md +74 -0
  17. package/agents/angular-reviewer/configs/claude.yaml +3 -0
  18. package/agents/angular-reviewer/configs/codex.yaml +2 -0
  19. package/agents/angular-reviewer/configs/opencode.yaml +5 -0
  20. package/dist/adapters/claude/index.js +45 -0
  21. package/dist/adapters/codex/index.js +46 -0
  22. package/dist/adapters/copilot/index.js +37 -0
  23. package/dist/adapters/cursor/index.js +43 -0
  24. package/dist/adapters/opencode/index.js +53 -0
  25. package/dist/src/cli.js +293 -0
  26. package/dist/src/index.js +6 -0
  27. package/dist/src/registry.js +13 -0
  28. package/dist/src/types.js +1 -0
  29. package/package.json +45 -0
  30. package/skills/architecture/injection-tokens/SKILL.md +82 -0
  31. package/skills/architecture/overlay-animation-lifecycle/SKILL.md +98 -0
  32. package/skills/components/content-projection-ng/SKILL.md +89 -0
  33. package/skills/components/dynamic-components/SKILL.md +74 -0
  34. package/skills/components/modern-host-bindings/SKILL.md +71 -0
  35. package/skills/components/viewchild-contentchild-signals/SKILL.md +66 -0
  36. package/skills/libraries/library-versioning/SKILL.md +49 -0
  37. package/skills/libraries/monorepo-ng-packagr/SKILL.md +69 -0
  38. package/skills/libraries/standalone-component-library/SKILL.md +103 -0
  39. package/skills/performance/control-flow-syntax/SKILL.md +94 -0
  40. package/skills/performance/defer-blocks/SKILL.md +83 -0
  41. package/skills/quality/pr-reviewer/SKILL.md +131 -0
  42. package/skills/quality/vitest-angular-components/SKILL.md +88 -0
  43. package/skills/reactivity/signals-effects/SKILL.md +63 -0
  44. package/skills/reactivity/signals-inputs-outputs/SKILL.md +76 -0
  45. package/skills/reactivity/signals-state-management/SKILL.md +70 -0
@@ -0,0 +1,146 @@
1
+ # Angular Migrator
2
+
3
+ You are an Angular migration specialist. You transform legacy Angular code to modern, performant patterns.
4
+
5
+ ## Core Skills
6
+
7
+ Load and follow the relevant skills from the project's `skills/` directory. The agent.yaml file lists which skills are relevant for this agent.
8
+
9
+ ## Migration Tasks
10
+
11
+ ### 1. Template Syntax Migration
12
+
13
+ Convert legacy directive syntax to built-in control flow:
14
+
15
+ ```html
16
+ <!-- BEFORE -->
17
+ <div *ngIf="condition">
18
+ <span *ngFor="let item of items">{{ item.name }}</span>
19
+ </div>
20
+
21
+ <!-- AFTER -->
22
+ @if (condition) {
23
+ <div>
24
+ @for (item of items; track item.id) {
25
+ <span>{{ item.name }}</span>
26
+ }
27
+ </div>
28
+ }
29
+ ```
30
+
31
+ **Rules:**
32
+
33
+ - Always add `track` expression to `@for` (use unique identifier or index)
34
+ - Replace `ngSwitch` with `@switch`
35
+ - Replace `[hidden]` with `@if` for conditional rendering
36
+ - Preserve existing CSS classes and styling
37
+
38
+ ### 2. Signal Migration
39
+
40
+ Convert class-based state to signals:
41
+
42
+ ```typescript
43
+ // BEFORE
44
+ export class MyComponent {
45
+ count = 0;
46
+ increment() { this.count++; }
47
+ }
48
+
49
+ // AFTER
50
+ export class MyComponent {
51
+ count = signal(0);
52
+ increment() { this.count.update(c => c + 1); }
53
+ }
54
+ ```
55
+
56
+ **Rules:**
57
+
58
+ - Convert `@Input()` to `input()` signal-based
59
+ - Convert `@Output()` to `output()` signal-based
60
+ - Use `computed()` for derived values
61
+ - Use `effect()` for side effects
62
+ - Replace `ngOnChanges` with `effect()` or `computed()`
63
+
64
+ ### 3. Standalone Component Migration
65
+
66
+ Convert NgModule-based components to standalone:
67
+
68
+ ```typescript
69
+ // BEFORE
70
+ @NgModule({
71
+ declarations: [MyComponent],
72
+ imports: [CommonModule, RouterModule]
73
+ })
74
+ export class MyModule {}
75
+
76
+ // AFTER
77
+ @Component({
78
+ standalone: true,
79
+ imports: [CommonModule, RouterModule],
80
+ template: `...`
81
+ })
82
+ export class MyComponent {}
83
+ ```
84
+
85
+ ### 4. Host Bindings Modernization
86
+
87
+ Migrate host metadata to decorator-based:
88
+
89
+ ```typescript
90
+ // BEFORE
91
+ @Component({
92
+ host: { '(click)': 'onClick($event)', '[class.active]': 'isActive' }
93
+ })
94
+
95
+ // AFTER
96
+ @Component({
97
+ host: {
98
+ '(click)': 'onClick($event)',
99
+ '[class.active]': 'isActive'
100
+ }
101
+ })
102
+ // Keep host metadata, but ensure modern syntax
103
+ ```
104
+
105
+ ## Execution Flow
106
+
107
+ 1. **Analyze** the provided files
108
+ 2. **Identify** all migration opportunities
109
+ 3. **Apply** migrations in order:
110
+ - Template syntax first (lowest risk)
111
+ - Standalone conversion second
112
+ - Signal migration third (highest impact)
113
+ - Host bindings last
114
+ 4. **Verify** the migration doesn't break existing functionality
115
+ 5. **Report** what was changed and what remains
116
+
117
+ ## Output Format
118
+
119
+ ```md
120
+ # Migración Angular Completada
121
+
122
+ ## Archivos modificados
123
+ - `path/to/file.ts` — [migration type]
124
+
125
+ ## Cambios realizados
126
+ ### Template Syntax
127
+ - [List of conversions]
128
+
129
+ ### Signals
130
+ - [List of signal conversions]
131
+
132
+ ### Standalone
133
+ - [List of standalone conversions]
134
+
135
+ ## Pendiente (requiere revisión manual)
136
+ - [Items that need human verification]
137
+ ```
138
+
139
+ ## Rules
140
+
141
+ - Preserve all existing functionality
142
+ - Keep same CSS classes and selectors
143
+ - Maintain same API surface for consumers
144
+ - Add comments for complex migrations
145
+ - Never remove existing tests — update them
146
+ - Write in Spanish when the user communicates in Spanish
@@ -0,0 +1,3 @@
1
+ name: angular-migrator
2
+ description: Migrate legacy Angular code to modern patterns.
3
+ tools: Read, Grep, Glob, Edit, Write, Bash
@@ -0,0 +1,2 @@
1
+ name: angular-migrator
2
+ description: Migrate legacy Angular code to modern patterns.
@@ -0,0 +1,5 @@
1
+ description: Migrate legacy Angular code to modern patterns.
2
+ mode: subagent
3
+ permission:
4
+ edit: allow
5
+ bash: ask
@@ -0,0 +1,74 @@
1
+ # Angular Code Reviewer
2
+
3
+ You are a senior Angular architect reviewing code for quality, performance, and modern patterns.
4
+
5
+ ## Core Skills
6
+
7
+ Load and follow the relevant skills from the project's `skills/` directory. The agent.yaml file lists which skills are relevant for this agent.
8
+
9
+ ## Review Checklist
10
+
11
+ For every file reviewed, check:
12
+
13
+ ### Performance
14
+
15
+ - [ ] Uses `@if`/`@for`/`@switch` instead of `*ngIf`/`*ngFor`/`*ngSwitch`
16
+ - [ ] Heavy components wrapped in `@defer` blocks
17
+ - [ ] Proper `@placeholder` sized to avoid layout shift
18
+ - [ ] No unnecessary `detectChanges()` calls
19
+
20
+ ### Reactivity
21
+
22
+ - [ ] Uses `signal()` for mutable state
23
+ - [ ] Uses `computed()` for derived state
24
+ - [ ] Uses `effect()` for side effects
25
+ - [ ] Signal-based `input()`/`output()` instead of decorators
26
+ - [ ] `toSignal()`/`toObservable()` used correctly
27
+
28
+ ### Architecture
29
+
30
+ - [ ] Proper `InjectionToken` usage for configuration
31
+ - [ ] Standalone components (no NgModule unless necessary)
32
+ - [ ] Functional `inject()` instead of constructor injection
33
+ - [ ] Clean dependency hierarchy
34
+
35
+ ### Testing
36
+
37
+ - [ ] Tests exist for new/modified components
38
+ - [ ] Tests use Vitest patterns (not Jasmine/Karma)
39
+ - [ ] Signal-based testing with `fixture.detectChanges()`
40
+
41
+ ## Output Format
42
+
43
+ ```md
44
+ # Revisión de Código Angular
45
+
46
+ **Archivo:** `path/to/file.ts`
47
+ **Fecha:** YYYY-MM-DD
48
+
49
+ ---
50
+
51
+ ## ✅ Lo que está bien
52
+ - [Positive observations]
53
+
54
+ ## 🔴 Blockers
55
+ - [Issues that must be fixed]
56
+
57
+ ## 🟡 Sugerencias
58
+ - [Improvements that are recommended]
59
+
60
+ ## 📊 Resumen
61
+ | Nivel | Cantidad |
62
+ |---|---|
63
+ | 🔴 Blockers | N |
64
+ | 🟡 Sugerencias | N |
65
+ | 🟢 Sin problemas | N |
66
+ ```
67
+
68
+ ## Rules
69
+
70
+ - Only review the files provided in context
71
+ - Be concise and direct
72
+ - Provide code examples for fixes
73
+ - Focus on Angular-specific patterns
74
+ - Write in Spanish when the user communicates in Spanish
@@ -0,0 +1,3 @@
1
+ name: angular-reviewer
2
+ description: Review Angular PRs and code for best practices, performance issues, and modern patterns.
3
+ tools: Read, Grep, Glob, Edit, Write, Bash
@@ -0,0 +1,2 @@
1
+ name: angular-reviewer
2
+ description: Review Angular PRs and code for best practices, performance issues, and modern patterns.
@@ -0,0 +1,5 @@
1
+ description: Review Angular PRs and code for best practices, performance issues, and modern patterns.
2
+ mode: subagent
3
+ permission:
4
+ edit: allow
5
+ bash: ask
@@ -0,0 +1,45 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { parse as parseYaml } from 'yaml';
4
+ const DEFAULT_CONFIG = {
5
+ description: 'Angular agent for Claude',
6
+ name: 'angular-agent',
7
+ tools: 'Read, Grep, Glob, Edit, Write, Bash',
8
+ };
9
+ function loadConfigFromAgent(agentBasePath, ai) {
10
+ const configPath = join(agentBasePath, 'configs', `${ai}.yaml`);
11
+ try {
12
+ return parseYaml(readFileSync(configPath, 'utf-8'));
13
+ }
14
+ catch {
15
+ return {};
16
+ }
17
+ }
18
+ export function createClaudeAdapter() {
19
+ return {
20
+ name: 'claude',
21
+ description: 'Claude Code',
22
+ defaultDir: '.claude/agents',
23
+ extension: 'md',
24
+ generate(agent, config) {
25
+ const agentConfig = loadConfigFromAgent(agent.basePath, 'claude');
26
+ const cfg = { ...DEFAULT_CONFIG, ...agentConfig, ...config };
27
+ const frontmatter = [
28
+ '---',
29
+ `name: ${cfg.name || agent.metadata.name}`,
30
+ `description: ${cfg.description || agent.metadata.description}`,
31
+ `tools: ${cfg.tools || 'Read, Grep, Glob, Edit, Write, Bash'}`,
32
+ '---',
33
+ '',
34
+ ];
35
+ return {
36
+ filename: `${agent.metadata.name}.md`,
37
+ content: frontmatter.join('\n') + agent.instructions.content,
38
+ };
39
+ },
40
+ getInstallPath(name, targetDir) {
41
+ const base = targetDir || this.defaultDir;
42
+ return `${base}/${name}.${this.extension}`;
43
+ },
44
+ };
45
+ }
@@ -0,0 +1,46 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { parse as parseYaml } from 'yaml';
4
+ const DEFAULT_CONFIG = {
5
+ description: 'Angular agent for Codex',
6
+ name: 'angular-agent',
7
+ };
8
+ function loadConfigFromAgent(agentBasePath, ai) {
9
+ const configPath = join(agentBasePath, 'configs', `${ai}.yaml`);
10
+ try {
11
+ return parseYaml(readFileSync(configPath, 'utf-8'));
12
+ }
13
+ catch {
14
+ return {};
15
+ }
16
+ }
17
+ export function createCodexAdapter() {
18
+ return {
19
+ name: 'codex',
20
+ description: 'OpenAI Codex CLI',
21
+ defaultDir: '.codex/agents',
22
+ extension: 'toml',
23
+ generate(agent, config) {
24
+ const agentConfig = loadConfigFromAgent(agent.basePath, 'codex');
25
+ const cfg = { ...DEFAULT_CONFIG, ...agentConfig, ...config };
26
+ const escapedInstructions = agent.instructions.content
27
+ .replace(/\\/g, '\\\\')
28
+ .replace(/"/g, '\\"')
29
+ .replace(/\n/g, '\\n');
30
+ const content = [
31
+ `name = "${cfg.name || agent.metadata.name}"`,
32
+ `description = "${cfg.description || agent.metadata.description}"`,
33
+ `developer_instructions = "${escapedInstructions}"`,
34
+ '',
35
+ ].join('\n');
36
+ return {
37
+ filename: `${agent.metadata.name}.toml`,
38
+ content,
39
+ };
40
+ },
41
+ getInstallPath(name, targetDir) {
42
+ const base = targetDir || this.defaultDir;
43
+ return `${base}/${name}.${this.extension}`;
44
+ },
45
+ };
46
+ }
@@ -0,0 +1,37 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { parse as parseYaml } from 'yaml';
4
+ const DEFAULT_CONFIG = {
5
+ description: 'Angular agent for GitHub Copilot',
6
+ name: 'angular-agent',
7
+ };
8
+ function loadConfigFromAgent(agentBasePath, ai) {
9
+ const configPath = join(agentBasePath, 'configs', `${ai}.yaml`);
10
+ try {
11
+ return parseYaml(readFileSync(configPath, 'utf-8'));
12
+ }
13
+ catch {
14
+ return {};
15
+ }
16
+ }
17
+ export function createCopilotAdapter() {
18
+ return {
19
+ name: 'copilot',
20
+ description: 'GitHub Copilot',
21
+ defaultDir: '.github',
22
+ extension: 'md',
23
+ generate(agent, config) {
24
+ const agentConfig = loadConfigFromAgent(agent.basePath, 'copilot');
25
+ const cfg = { ...DEFAULT_CONFIG, ...agentConfig, ...config };
26
+ const header = `# ${cfg.name || agent.metadata.name}\n\n${cfg.description || agent.metadata.description}\n\n`;
27
+ return {
28
+ filename: `copilot-instructions-${agent.metadata.name}.md`,
29
+ content: header + agent.instructions.content,
30
+ };
31
+ },
32
+ getInstallPath(name, targetDir) {
33
+ const base = targetDir || this.defaultDir;
34
+ return `${base}/${name}-instructions.${this.extension}`;
35
+ },
36
+ };
37
+ }
@@ -0,0 +1,43 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { parse as parseYaml } from 'yaml';
4
+ const DEFAULT_CONFIG = {
5
+ description: 'Angular agent for Cursor',
6
+ name: 'angular-agent',
7
+ };
8
+ function loadConfigFromAgent(agentBasePath, ai) {
9
+ const configPath = join(agentBasePath, 'configs', `${ai}.yaml`);
10
+ try {
11
+ return parseYaml(readFileSync(configPath, 'utf-8'));
12
+ }
13
+ catch {
14
+ return {};
15
+ }
16
+ }
17
+ export function createCursorAdapter() {
18
+ return {
19
+ name: 'cursor',
20
+ description: 'Cursor AI IDE',
21
+ defaultDir: '.cursor/rules',
22
+ extension: 'mdc',
23
+ generate(agent, config) {
24
+ const agentConfig = loadConfigFromAgent(agent.basePath, 'cursor');
25
+ const cfg = { ...DEFAULT_CONFIG, ...agentConfig, ...config };
26
+ const frontmatter = [
27
+ '---',
28
+ `description: ${cfg.description || agent.metadata.description}`,
29
+ 'alwaysApply: false',
30
+ '---',
31
+ '',
32
+ ];
33
+ return {
34
+ filename: `${agent.metadata.name}.mdc`,
35
+ content: frontmatter.join('\n') + agent.instructions.content,
36
+ };
37
+ },
38
+ getInstallPath(name, targetDir) {
39
+ const base = targetDir || this.defaultDir;
40
+ return `${base}/${name}.${this.extension}`;
41
+ },
42
+ };
43
+ }
@@ -0,0 +1,53 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { parse as parseYaml } from 'yaml';
4
+ const DEFAULT_CONFIG = {
5
+ description: 'Angular agent for OpenCode',
6
+ mode: 'subagent',
7
+ permission: {
8
+ edit: 'allow',
9
+ bash: 'ask',
10
+ },
11
+ };
12
+ function loadConfigFromAgent(agentBasePath, ai) {
13
+ const configPath = join(agentBasePath, 'configs', `${ai}.yaml`);
14
+ try {
15
+ return parseYaml(readFileSync(configPath, 'utf-8'));
16
+ }
17
+ catch {
18
+ return {};
19
+ }
20
+ }
21
+ export function createOpenCodeAdapter() {
22
+ return {
23
+ name: 'opencode',
24
+ description: 'OpenCode AI coding agent',
25
+ defaultDir: '.opencode/agents',
26
+ extension: 'md',
27
+ generate(agent, config) {
28
+ const agentConfig = loadConfigFromAgent(agent.basePath, 'opencode');
29
+ const cfg = { ...DEFAULT_CONFIG, ...agentConfig, ...config };
30
+ const frontmatter = [
31
+ '---',
32
+ `description: ${cfg.description || agent.metadata.description}`,
33
+ `mode: ${cfg.mode || 'subagent'}`,
34
+ ];
35
+ if (cfg.permission) {
36
+ frontmatter.push('permission:');
37
+ const perm = cfg.permission;
38
+ for (const [key, value] of Object.entries(perm)) {
39
+ frontmatter.push(` ${key}: ${value}`);
40
+ }
41
+ }
42
+ frontmatter.push('---', '');
43
+ return {
44
+ filename: `${agent.metadata.name}.md`,
45
+ content: frontmatter.join('\n') + agent.instructions.content,
46
+ };
47
+ },
48
+ getInstallPath(name, targetDir) {
49
+ const base = targetDir || this.defaultDir;
50
+ return `${base}/${name}.${this.extension}`;
51
+ },
52
+ };
53
+ }